diff --git a/Cargo.lock b/Cargo.lock index e75e1bc76..6faa0cf32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,7 @@ dependencies = [ "ethsync 1.3.0", "fdlimit 0.1.0", "hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "json-ipc-server 0.2.3 (git+https://github.com/ethcore/json-ipc-server.git)", + "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -164,7 +164,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -261,6 +261,7 @@ dependencies = [ "ethjson 0.1.0", "ethstore 0.1.0", "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -278,21 +279,21 @@ dependencies = [ "clippy 0.0.77 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-rpc 1.3.0", "ethcore-util 1.3.0", - "hyper 0.9.3 (git+https://github.com/ethcore/hyper)", + "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", - "parity-dapps-builtins 0.5.1 (git+https://github.com/ethcore/parity-dapps-builtins-rs.git)", - "parity-dapps-status 0.5.0 (git+https://github.com/ethcore/parity-dapps-status-rs.git)", + "parity-dapps-builtins 0.5.2 (git+https://github.com/ethcore/parity-dapps-builtins-rs.git)", + "parity-dapps-status 0.5.1 (git+https://github.com/ethcore/parity-dapps-status-rs.git)", "parity-dapps-wallet 0.6.1 (git+https://github.com/ethcore/parity-dapps-wallet-rs.git)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -344,7 +345,7 @@ dependencies = [ "ethcore-util 1.3.0", "ethjson 0.1.0", "ethsync 1.3.0", - "json-ipc-server 0.2.3 (git+https://github.com/ethcore/json-ipc-server.git)", + "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -366,16 +367,17 @@ dependencies = [ "ethcore-util 1.3.0", "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-minimal-sysui 0.1.0 (git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git)", + "parity-minimal-sysui 0.2.0 (git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "ws 0.5.1 (git+https://github.com/ethcore/ws-rs.git)", + "ws 0.5.0 (git+https://github.com/ethcore/ws-rs.git?branch=stable)", ] [[package]] name = "ethcore-util" version = "1.3.0" dependencies = [ + "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "bigint 0.1.0", "chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", @@ -546,23 +548,22 @@ dependencies = [ [[package]] name = "hyper" -version = "0.9.3" -source = "git+https://github.com/ethcore/hyper#dbb4cf160ebf242f7f0459d547c40e9e6877ccf4" +version = "0.9.4" +source = "git+https://github.com/ethcore/hyper#9e346c1d4bc30cd4142dea9d8a0b117d30858ca4" dependencies = [ "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rotor 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rotor 0.6.3 (git+https://github.com/ethcore/rotor)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "spmc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -595,8 +596,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "json-ipc-server" -version = "0.2.3" -source = "git+https://github.com/ethcore/json-ipc-server.git#bfe16b66b2e9412d153b1ea53bc078d74037da7f" +version = "0.2.4" +source = "git+https://github.com/ethcore/json-ipc-server.git#902b031b8f50a59ecb4f389cbec1d264a98556bc" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -630,9 +631,9 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "5.1.0" -source = "git+https://github.com/ethcore/jsonrpc-http-server.git#0c99d308bc15e8fae50642eff77a3e1fd7610652" +source = "git+https://github.com/ethcore/jsonrpc-http-server.git#e59c2fbaca499620874023755cb91f05b858ffe7" dependencies = [ - "hyper 0.9.3 (git+https://github.com/ethcore/hyper)", + "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -712,26 +713,10 @@ dependencies = [ "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "mio" -version = "0.5.0" -source = "git+https://github.com/carllerche/mio.git#f4aa49a9d2c4507fb33a4533d5238e0365f67c99" -dependencies = [ - "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.5.0-pre (git+https://github.com/carllerche/nix-rust?rev=c4257f8a76)", - "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "mio" version = "0.5.1" -source = "git+https://github.com/ethcore/mio?branch=v0.5.x#1fc881771fb8c2517317b4f805d7b88235be422b" +source = "git+https://github.com/ethcore/mio?branch=v0.5.x#3842d3b250ffd7bd9b16f9586b875ddcbac2b0dd" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -801,15 +786,6 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "nix" -version = "0.5.0-pre" -source = "git+https://github.com/carllerche/nix-rust?rev=c4257f8a76#c4257f8a76b69b0d2e9a001d83e4bef67c03b23f" -dependencies = [ - "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "nix" version = "0.5.0" @@ -935,16 +911,16 @@ dependencies = [ [[package]] name = "parity-dapps-builtins" -version = "0.5.1" -source = "git+https://github.com/ethcore/parity-dapps-builtins-rs.git#7408838e8ca3b57c6b0cf5da2e31e0e275959955" +version = "0.5.2" +source = "git+https://github.com/ethcore/parity-dapps-builtins-rs.git#01af2091d5d70dfe0aecbfd96308f0ae79fc61e6" dependencies = [ "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", ] [[package]] name = "parity-dapps-status" -version = "0.5.0" -source = "git+https://github.com/ethcore/parity-dapps-status-rs.git#0cdd3512004e403aff7da3b8c16ba0bf5d6c911c" +version = "0.5.1" +source = "git+https://github.com/ethcore/parity-dapps-status-rs.git#110ef2e66142ec8dc15fc40b8ddda5ed3bcfc1fb" dependencies = [ "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", ] @@ -959,8 +935,8 @@ dependencies = [ [[package]] name = "parity-minimal-sysui" -version = "0.1.0" -source = "git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git#cc5ea4bd786982f0509a8d3d5deb4217c659af85" +version = "0.2.0" +source = "git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git#996c9f3f0ebedc727aecb4ece191154e956ae292" [[package]] name = "phf" @@ -1057,7 +1033,7 @@ dependencies = [ [[package]] name = "quick-error" -version = "0.2.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1103,7 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rocksdb" version = "0.4.5" -source = "git+https://github.com/ethcore/rust-rocksdb#6f3c68f5f075433d206be4af6a620651cd9f8541" +source = "git+https://github.com/ethcore/rust-rocksdb#9be41e05923616dfa28741c58b22776d479751e6" dependencies = [ "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)", @@ -1112,7 +1088,7 @@ dependencies = [ [[package]] name = "rocksdb-sys" version = "0.3.0" -source = "git+https://github.com/ethcore/rust-rocksdb#6f3c68f5f075433d206be4af6a620651cd9f8541" +source = "git+https://github.com/ethcore/rust-rocksdb#9be41e05923616dfa28741c58b22776d479751e6" dependencies = [ "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1121,12 +1097,11 @@ dependencies = [ [[package]] name = "rotor" version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/ethcore/rotor#e63d45137b2eb66d1e085a7c6321a5db8b187576" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)", + "quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1249,7 +1224,7 @@ dependencies = [ [[package]] name = "spmc" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1408,7 +1383,7 @@ dependencies = [ [[package]] name = "url" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1472,16 +1447,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ws" -version = "0.5.1" -source = "git+https://github.com/ethcore/ws-rs.git#d5745df8ea1ab82cd2b844f15ca1ac759e7aa9f5" +version = "0.5.0" +source = "git+https://github.com/ethcore/ws-rs.git?branch=stable#a876fc115c3ef50a17c8822c9bd2f6e94473e005" dependencies = [ "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.5.0 (git+https://github.com/carllerche/mio.git)", + "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/appveyor.yml b/appveyor.yml index b980aba42..212b26ea9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,13 +1,27 @@ environment: matrix: - TARGET: x86_64-pc-windows-msvc + cert: + secure: ESPpYVVAMG1fbJx6kq4ct/g9SQTXac4Hs6xXr6Oh4Zrk2dwYglNjxmzErdPnvu7gs/gekzrJ6KEQHYRc+5+4dKg6rRADQ681NLVx9vOggBs= + certpass: + secure: 0BgXJqxq9Ei34/hZ7121FQ== + keyfile: C:\users\appveyor\Certificates.p12 + +branches: + only: + - master + - /^beta-.*$/ + - /^stable-.*$/ + - /^beta$/ + - /^stable$/ + install: - git submodule update --init --recursive - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.9.0-x86_64-pc-windows-msvc.exe" - ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll - ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe - rust-1.9.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS + - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin - rustc -V - cargo -V @@ -18,7 +32,10 @@ test_script: after_test: - cargo build --verbose --release + - ps: if($env:cert) { Start-FileDownload $env:cert -FileName $env:keyfile } + - ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass target\release\parity.exe } - makensis.exe nsis\installer.nsi + - ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass nsis\installer.exe } artifacts: - path: nsis\installer.exe diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 0476e9919..d33bd6702 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -22,8 +22,8 @@ ethcore-rpc = { path = "../rpc" } ethcore-util = { path = "../util" } parity-dapps = { git = "https://github.com/ethcore/parity-dapps-rs.git", version = "0.3" } # List of apps -parity-dapps-status = { git = "https://github.com/ethcore/parity-dapps-status-rs.git", version = "0.5.0" } -parity-dapps-builtins = { git = "https://github.com/ethcore/parity-dapps-builtins-rs.git", version = "0.5.0" } +parity-dapps-status = { git = "https://github.com/ethcore/parity-dapps-status-rs.git", version = "0.5.1" } +parity-dapps-builtins = { git = "https://github.com/ethcore/parity-dapps-builtins-rs.git", version = "0.5.2" } parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs.git", version = "0.6.0", optional = true } parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.4.0", optional = true } parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.3.0", optional = true } diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs index 592bc7f8f..075211e58 100644 --- a/dapps/src/endpoint.rs +++ b/dapps/src/endpoint.rs @@ -42,11 +42,11 @@ pub struct EndpointInfo { pub trait Endpoint : Send + Sync { fn info(&self) -> Option<&EndpointInfo> { None } - fn to_handler(&self, path: EndpointPath) -> Box>; + fn to_handler(&self, path: EndpointPath) -> Box + Send>; } pub type Endpoints = BTreeMap>; -pub type Handler = server::Handler; +pub type Handler = server::Handler + Send; pub struct ContentHandler { content: String, @@ -65,7 +65,7 @@ impl ContentHandler { } impl server::Handler for ContentHandler { - fn on_request(&mut self, _request: server::Request) -> Next { + fn on_request(&mut self, _request: server::Request) -> Next { Next::write() } diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index a7fbd5963..22b075afd 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -132,9 +132,16 @@ impl Server { special.clone(), authorization.clone(), )) - .map(|l| Server { - server: Some(l), - panic_handler: panic_handler, + .map(|(l, srv)| { + + ::std::thread::spawn(move || { + srv.run(); + }); + + Server { + server: Some(l), + panic_handler: panic_handler, + } }) .map_err(ServerError::from) } diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index 5167c85c8..c3ec354e0 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -86,7 +86,7 @@ impl PageHandler { } impl server::Handler for PageHandler { - fn on_request(&mut self, req: server::Request) -> Next { + fn on_request(&mut self, req: server::Request) -> Next { self.file = match *req.uri() { RequestUri::AbsolutePath(ref path) => { self.app.file(&self.extract_path(path)) diff --git a/dapps/src/router/auth.rs b/dapps/src/router/auth.rs index d9933b51c..9459d0719 100644 --- a/dapps/src/router/auth.rs +++ b/dapps/src/router/auth.rs @@ -27,13 +27,13 @@ pub enum Authorized { /// Authorization was successful. Yes, /// Unsuccessful authorization. Handler for further work is returned. - No(Box>), + No(Box + Send>), } /// Authorization interface pub trait Authorization : Send + Sync { /// Checks if authorization is valid. - fn is_authorized(&self, req: &server::Request)-> Authorized; + fn is_authorized(&self, req: &server::Request)-> Authorized; } /// HTTP Basic Authorization handler @@ -45,13 +45,13 @@ pub struct HttpBasicAuth { pub struct NoAuth; impl Authorization for NoAuth { - fn is_authorized(&self, _req: &server::Request)-> Authorized { + fn is_authorized(&self, _req: &server::Request)-> Authorized { Authorized::Yes } } impl Authorization for HttpBasicAuth { - fn is_authorized(&self, req: &server::Request) -> Authorized { + fn is_authorized(&self, req: &server::Request) -> Authorized { let auth = self.check_auth(&req); match auth { @@ -89,7 +89,7 @@ impl HttpBasicAuth { self.users.get(&username.to_owned()).map_or(false, |pass| pass == password) } - fn check_auth(&self, req: &server::Request) -> Access { + fn check_auth(&self, req: &server::Request) -> Access { match req.headers().get::>() { Some(&header::Authorization( header::Basic { ref username, password: Some(ref password) } @@ -105,7 +105,7 @@ pub struct UnauthorizedHandler { } impl server::Handler for UnauthorizedHandler { - fn on_request(&mut self, _request: server::Request) -> Next { + fn on_request(&mut self, _request: server::Request) -> Next { Next::write() } @@ -141,7 +141,7 @@ impl server::Handler for UnauthorizedHandler { pub struct AuthRequiredHandler; impl server::Handler for AuthRequiredHandler { - fn on_request(&mut self, _request: server::Request) -> Next { + fn on_request(&mut self, _request: server::Request) -> Next { Next::write() } diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index 12738d79d..0cb0d38d0 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -49,12 +49,12 @@ pub struct Router { endpoints: Arc, special: Arc>>, authorization: Arc, - handler: Box>, + handler: Box + Send>, } impl server::Handler for Router { - fn on_request(&mut self, req: server::Request) -> Next { + fn on_request(&mut self, req: server::Request) -> Next { // Check authorization let auth = self.authorization.is_authorized(&req); @@ -124,7 +124,7 @@ impl Router { } } -fn extract_url(req: &server::Request) -> Option { +fn extract_url(req: &server::Request) -> Option { match *req.uri() { uri::RequestUri::AbsoluteUri(ref url) => { match Url::from_generic_url(url.clone()) { diff --git a/dapps/src/router/redirect.rs b/dapps/src/router/redirect.rs index 6faf9d0c8..6d738115d 100644 --- a/dapps/src/router/redirect.rs +++ b/dapps/src/router/redirect.rs @@ -33,7 +33,7 @@ impl Redirection { } impl server::Handler for Redirection { - fn on_request(&mut self, _request: server::Request) -> Next { + fn on_request(&mut self, _request: server::Request) -> Next { Next::write() } diff --git a/docker/centos/Dockerfile b/docker/centos/Dockerfile index 56015422c..9fd33f4ff 100644 --- a/docker/centos/Dockerfile +++ b/docker/centos/Dockerfile @@ -20,6 +20,8 @@ g++ -v # build parity RUN git clone https://github.com/ethcore/parity && \ cd parity&&\ + git checkout beta && \ + git pull && \ ls -a&&\ cargo build --release --verbose && \ ls /build/parity/target/release/parity && \ diff --git a/docker/ubuntu-aarch64/Dockerfile b/docker/ubuntu-aarch64/Dockerfile index 3212f84d0..38ed4c9b1 100644 --- a/docker/ubuntu-aarch64/Dockerfile +++ b/docker/ubuntu-aarch64/Dockerfile @@ -36,6 +36,8 @@ ENV CC aarch64-linux-gnu-gcc # build parity RUN git clone https://github.com/ethcore/parity && \ cd parity && \ + git checkout beta && \ + git pull && \ mkdir -p .cargo && \ echo '[target.aarch64-unknown-linux-gnu]\n\ linker = "aarch64-linux-gnu-gcc"\n'\ diff --git a/docker/ubuntu-arm/Dockerfile b/docker/ubuntu-arm/Dockerfile index fab325e5e..b59573a0f 100644 --- a/docker/ubuntu-arm/Dockerfile +++ b/docker/ubuntu-arm/Dockerfile @@ -36,6 +36,8 @@ ENV CC arm-linux-gnueabihf-gcc # build parity RUN git clone https://github.com/ethcore/parity && \ cd parity && \ + git checkout beta && \ + git pull && \ mkdir -p .cargo && \ echo '[target.armv7-unknown-linux-gnueabihf]\n\ linker = "arm-linux-gnueabihf-gcc"\n'\ diff --git a/docker/ubuntu-jit/Dockerfile b/docker/ubuntu-jit/Dockerfile index 89c38cee7..666b16904 100644 --- a/docker/ubuntu-jit/Dockerfile +++ b/docker/ubuntu-jit/Dockerfile @@ -47,6 +47,8 @@ g++ -v # build parity RUN git clone https://github.com/ethcore/parity && \ cd parity && \ + git checkout beta && \ + git pull && \ cargo build --release --features ethcore/jit --verbose && \ ls /build/parity/target/release/parity && \ strip /build/parity/target/release/parity diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 9999909c3..4c82e1ecc 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -27,7 +27,9 @@ g++ -v # build parity RUN git clone https://github.com/ethcore/parity && \ cd parity && \ + git checkout beta && \ + git pull && \ cargo build --release --verbose && \ - ls /build/parity/target/release/parity && \ + ls /build/parity/target/release/parity && \ strip /build/parity/target/release/parity RUN file /build/parity/target/release/parity diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 2b56bf581..2d94aebab 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -32,6 +32,10 @@ bloomchain = "0.1" rayon = "0.3.1" ethstore = { path = "../ethstore" } +[dependencies.hyper] +git = "https://github.com/ethcore/hyper" +default-features = false + [features] jit = ["evmjit"] evm-debug = [] diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index d0628436d..2edbf87ae 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -108,6 +108,12 @@ impl Account { self.code_cache = code; } + /// Reset this account's code to the given code. + pub fn reset_code(&mut self, code: Bytes) { + self.code_hash = None; + self.init_code(code); + } + /// Set (and cache) the contents of the trie's storage at `key` to `value`. pub fn set_storage(&mut self, key: H256, value: H256) { self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value)); @@ -336,6 +342,21 @@ mod tests { assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb"); } + #[test] + fn reset_code() { + let mut a = Account::new_contract(69.into(), 0.into()); + let mut db = MemoryDB::new(); + let mut db = AccountDBMut::new(&mut db, &Address::new()); + a.init_code(vec![0x55, 0x44, 0xffu8]); + assert_eq!(a.code_hash(), SHA3_EMPTY); + a.commit_code(&mut db); + assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb"); + a.reset_code(vec![0x55]); + assert_eq!(a.code_hash(), SHA3_EMPTY); + a.commit_code(&mut db); + assert_eq!(a.code_hash().hex(), "37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be"); + } + #[test] fn rlpio() { let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); @@ -348,7 +369,6 @@ mod tests { #[test] fn new_account() { - let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); assert_eq!(a.balance(), &U256::from(69u8)); @@ -359,7 +379,6 @@ mod tests { #[test] fn create_account() { - let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); } diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 103435f40..13a2024d9 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -164,6 +164,12 @@ pub trait IsBlock { fn uncles(&self) -> &Vec
{ &self.block().base.uncles } } +/// Trait for a object that has a state database. +pub trait Drain { + /// Drop this object and return the underlieing database. + fn drain(self) -> Box; +} + impl IsBlock for ExecutedBlock { fn block(&self) -> &ExecutedBlock { self } } @@ -436,9 +442,11 @@ impl LockedBlock { _ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }), } } +} +impl Drain for LockedBlock { /// Drop this object and return the underlieing database. - pub fn drain(self) -> Box { self.block.state.drop().1 } + fn drain(self) -> Box { self.block.state.drop().1 } } impl SealedBlock { @@ -450,9 +458,11 @@ impl SealedBlock { block_rlp.append_raw(&self.uncle_bytes, 1); block_rlp.out() } +} +impl Drain for SealedBlock { /// Drop this object and return the underlieing database. - pub fn drain(self) -> Box { self.block.state.drop().1 } + fn drain(self) -> Box { self.block.state.drop().1 } } impl IsBlock for SealedBlock { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 4ccfb7360..d5e509e62 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -16,7 +16,6 @@ //! Blockchain database client. -use std::marker::PhantomData; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use util::*; @@ -30,7 +29,8 @@ use engine::Engine; use views::HeaderView; use service::{NetSyncMessage, SyncMessage}; use env_info::LastHashes; -use verification::*; +use verification; +use verification::{PreverifiedBlock, Verifier}; use block::*; use transaction::{LocalizedTransaction, SignedTransaction, Action}; use blockchain::extras::TransactionAddress; @@ -38,7 +38,7 @@ use filter::Filter; use log_entry::LocalizedLogEntry; use block_queue::{BlockQueue, BlockQueueInfo}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; -use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics}; +use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics}; use client::Error as ClientError; use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; @@ -83,7 +83,7 @@ impl ClientReport { /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. -pub struct Client where V: Verifier { +pub struct Client { chain: Arc, tracedb: Arc>, engine: Arc>, @@ -92,7 +92,7 @@ pub struct Client where V: Verifier { report: RwLock, import_lock: Mutex<()>, panic_handler: Arc, - verifier: PhantomData, + verifier: Box, vm_factory: Arc, miner: Arc, io_channel: IoChannel, @@ -107,13 +107,6 @@ const HISTORY: u64 = 1200; // of which you actually want force an upgrade. const CLIENT_DB_VER_STR: &'static str = "5.3"; -impl Client { - /// Create a new client with given spec and DB path. - pub fn new(config: ClientConfig, spec: Spec, path: &Path, miner: Arc, message_channel: IoChannel ) -> Result, ClientError> { - Client::::new_with_verifier(config, spec, path, miner, message_channel) - } -} - /// Get the path for the databases given the root path and information on the databases. pub fn get_db_path(path: &Path, pruning: journaldb::Algorithm, genesis_hash: H256) -> PathBuf { let mut dir = path.to_path_buf(); @@ -131,25 +124,35 @@ pub fn append_path(path: &Path, item: &str) -> String { p.to_str().unwrap().to_owned() } -impl Client where V: Verifier { +impl Client { /// Create a new client with given spec and DB path and custom verifier. - pub fn new_with_verifier( + pub fn new( config: ClientConfig, spec: Spec, path: &Path, miner: Arc, message_channel: IoChannel) - -> Result>, ClientError> + -> Result, ClientError> { let path = get_db_path(path, config.pruning, spec.genesis_header().hash()); let gb = spec.genesis_block(); let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path)); let tracedb = Arc::new(try!(TraceDB::new(config.tracing, &path, chain.clone()))); + let mut state_db_config = match config.db_cache_size { + None => DatabaseConfig::default(), + Some(cache_size) => DatabaseConfig::with_cache(cache_size), + }; + + if config.db_compaction == DatabaseCompactionProfile::HDD { + state_db_config = state_db_config.compaction(CompactionProfile::hdd()); + } + let mut state_db = journaldb::new( &append_path(&path, "state"), config.pruning, - config.db_cache_size); + state_db_config + ); if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) { state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB"); @@ -170,7 +173,7 @@ impl Client where V: Verifier { report: RwLock::new(Default::default()), import_lock: Mutex::new(()), panic_handler: panic_handler, - verifier: PhantomData, + verifier: verification::new(config.verifier_type), vm_factory: Arc::new(EvmFactory::new(config.vm_type)), miner: miner, io_channel: message_channel, @@ -212,7 +215,7 @@ impl Client where V: Verifier { } // Verify Block Family - let verify_family_result = V::verify_block_family(&header, &block.bytes, engine, self.chain.deref()); + let verify_family_result = self.verifier.verify_block_family(&header, &block.bytes, engine, self.chain.deref()); if let Err(e) = verify_family_result { warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); @@ -238,7 +241,7 @@ impl Client where V: Verifier { // Final Verification let locked_block = enact_result.unwrap(); - if let Err(e) = V::verify_block_final(&header, locked_block.block().header()) { + if let Err(e) = self.verifier.verify_block_final(&header, locked_block.block().header()) { warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); } @@ -246,7 +249,7 @@ impl Client where V: Verifier { Ok(locked_block) } - fn calculate_enacted_retracted(&self, import_results: Vec) -> (Vec, Vec) { + fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec, Vec) { fn map_to_vec(map: Vec<(H256, bool)>) -> Vec { map.into_iter().map(|(k, _v)| k).collect() } @@ -256,12 +259,12 @@ impl Client where V: Verifier { // could be retracted in import `k+1`. This is why to understand if after all inserts // the block is enacted or retracted we iterate over all routes and at the end final state // will be in the hashmap - let map = import_results.into_iter().fold(HashMap::new(), |mut map, route| { - for hash in route.enacted { - map.insert(hash, true); + let map = import_results.iter().fold(HashMap::new(), |mut map, route| { + for hash in &route.enacted { + map.insert(hash.clone(), true); } - for hash in route.retracted { - map.insert(hash, false); + for hash in &route.retracted { + map.insert(hash.clone(), false); } map }); @@ -296,38 +299,12 @@ impl Client where V: Verifier { let closed_block = self.check_and_close_block(&block); if let Err(_) = closed_block { invalid_blocks.insert(header.hash()); - break; + continue; } + let closed_block = closed_block.unwrap(); imported_blocks.push(header.hash()); - // Are we committing an era? - let ancient = if header.number() >= HISTORY { - let n = header.number() - HISTORY; - Some((n, self.chain.block_hash(n).unwrap())) - } else { - None - }; - - // Commit results - let closed_block = closed_block.unwrap(); - let receipts = closed_block.block().receipts().clone(); - let traces = From::from(closed_block.block().traces().clone().unwrap_or_else(Vec::new)); - - closed_block.drain() - .commit(header.number(), &header.hash(), ancient) - .expect("State DB commit failed."); - - // And update the chain after commit to prevent race conditions - // (when something is in chain but you are not able to fetch details) - let route = self.chain.insert_block(&block.bytes, receipts); - self.tracedb.import(TraceImportRequest { - traces: traces, - block_hash: header.hash(), - block_number: header.number(), - enacted: route.enacted.clone(), - retracted: route.retracted.len() - }); - + let route = self.commit_block(closed_block, &header.hash(), &block.bytes); import_results.push(route); self.report.write().unwrap().accrue_block(&block); @@ -348,7 +325,7 @@ impl Client where V: Verifier { { if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() { - let (enacted, retracted) = self.calculate_enacted_retracted(import_results); + let (enacted, retracted) = self.calculate_enacted_retracted(&import_results); if self.queue_info().is_empty() { self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted); @@ -359,19 +336,50 @@ impl Client where V: Verifier { invalid: invalid_blocks, enacted: enacted, retracted: retracted, - })).unwrap(); + sealed: Vec::new(), + })).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e)); } } - { - if self.chain_info().best_block_hash != original_best { - self.miner.update_sealing(self); - } + if self.chain_info().best_block_hash != original_best { + self.miner.update_sealing(self); } imported } + fn commit_block(&self, block: B, hash: &H256, block_data: &Bytes) -> ImportRoute where B: IsBlock + Drain { + let number = block.header().number(); + // Are we committing an era? + let ancient = if number >= HISTORY { + let n = number - HISTORY; + Some((n, self.chain.block_hash(n).unwrap())) + } else { + None + }; + + // Commit results + let receipts = block.receipts().clone(); + let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new)); + + // CHECK! I *think* this is fine, even if the state_root is equal to another + // already-imported block of the same number. + // TODO: Prove it with a test. + block.drain().commit(number, hash, ancient).expect("State DB commit failed."); + + // And update the chain after commit to prevent race conditions + // (when something is in chain but you are not able to fetch details) + let route = self.chain.insert_block(block_data, receipts); + self.tracedb.import(TraceImportRequest { + traces: traces, + block_hash: hash.clone(), + block_number: number, + enacted: route.enacted.clone(), + retracted: route.retracted.len() + }); + route + } + /// Import transactions from the IO queue pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize { let _timer = PerfTimer::new("import_queued_transactions"); @@ -473,7 +481,7 @@ impl Client where V: Verifier { } } -impl BlockChainClient for Client where V: Verifier { +impl BlockChainClient for Client { fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result { let header = self.block_header(BlockID::Latest).unwrap(); let view = HeaderView::new(&header); @@ -512,9 +520,6 @@ impl BlockChainClient for Client where V: Verifier { ret } - fn vm_factory(&self) -> &EvmFactory { - &self.vm_factory - } fn block_header(&self, id: BlockID) -> Option { Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec())) @@ -791,12 +796,12 @@ impl BlockChainClient for Client where V: Verifier { } } - fn all_transactions(&self) -> Vec { - self.miner.all_transactions() + fn pending_transactions(&self) -> Vec { + self.miner.pending_transactions() } } -impl MiningBlockChainClient for Client where V: Verifier { +impl MiningBlockChainClient for Client { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { let engine = self.engine.deref().deref(); let h = self.chain.best_block_hash(); @@ -826,6 +831,43 @@ impl MiningBlockChainClient for Client where V: Verifier { open_block } + + fn vm_factory(&self) -> &EvmFactory { + &self.vm_factory + } + + fn import_sealed_block(&self, block: SealedBlock) -> ImportResult { + let _import_lock = self.import_lock.lock(); + let _timer = PerfTimer::new("import_sealed_block"); + + let original_best = self.chain_info().best_block_hash; + + let h = block.header().hash(); + let number = block.header().number(); + + let block_data = block.rlp_bytes(); + let route = self.commit_block(block, &h, &block_data); + trace!(target: "client", "Imported sealed block #{} ({})", number, h); + + { + let (enacted, retracted) = self.calculate_enacted_retracted(&[route]); + self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted); + + self.io_channel.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks { + imported: vec![h.clone()], + invalid: vec![], + enacted: enacted, + retracted: retracted, + sealed: vec![h.clone()], + })).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e)); + } + + if self.chain_info().best_block_hash != original_best { + self.miner.update_sealing(self); + } + + Ok(h) + } } impl MayPanic for Client { diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 6353e324f..52a875a2f 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -18,8 +18,22 @@ pub use block_queue::BlockQueueConfig; pub use blockchain::Config as BlockChainConfig; pub use trace::{Config as TraceConfig, Switch}; pub use evm::VMType; +pub use verification::VerifierType; use util::journaldb; +/// Client state db compaction profile +#[derive(Debug, PartialEq)] +pub enum DatabaseCompactionProfile { + /// Default compaction profile + Default, + /// HDD or other slow storage io compaction profile + HDD, +} + +impl Default for DatabaseCompactionProfile { + fn default() -> Self { DatabaseCompactionProfile::Default } +} + /// Client configuration. Includes configs for all sub-systems. #[derive(Debug, Default)] pub struct ClientConfig { @@ -37,4 +51,8 @@ pub struct ClientConfig { pub name: String, /// State db cache-size if not default pub db_cache_size: Option, + /// State db compaction profile + pub db_compaction: DatabaseCompactionProfile, + /// Type of block verifier used by client. + pub verifier_type: VerifierType, } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 80625ad85..7f3c3bb3a 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -23,7 +23,7 @@ mod test_client; mod trace; pub use self::client::*; -pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig, Switch, VMType}; +pub use self::config::{ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType}; pub use self::error::Error; pub use types::ids::*; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; @@ -37,7 +37,7 @@ use util::numbers::U256; use util::Itertools; use blockchain::TreeRoute; use block_queue::BlockQueueInfo; -use block::OpenBlock; +use block::{OpenBlock, SealedBlock}; use header::{BlockNumber, Header}; use transaction::{LocalizedTransaction, SignedTransaction}; use log_entry::LocalizedLogEntry; @@ -172,9 +172,6 @@ pub trait BlockChainClient : Sync + Send { // TODO: should be able to accept blockchain location for call. fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result; - /// Returns EvmFactory. - fn vm_factory(&self) -> &EvmFactory; - /// Returns traces matching given filter. fn filter_traces(&self, filter: TraceFilter) -> Option>; @@ -197,7 +194,7 @@ pub trait BlockChainClient : Sync + Send { fn queue_transactions(&self, transactions: Vec); /// list all transactions - fn all_transactions(&self) -> Vec; + fn pending_transactions(&self) -> Vec; /// Get the gas price distribution. fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result, ()> { @@ -245,7 +242,7 @@ pub trait BlockChainClient : Sync + Send { } else { None } - } + } } /// Extended client interface used for mining @@ -253,4 +250,10 @@ pub trait MiningBlockChainClient : BlockChainClient { /// Returns OpenBlock prepared for closing. fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock; + + /// Returns EvmFactory. + fn vm_factory(&self) -> &EvmFactory; + + /// Import sealed block. Skips all verifications. + fn import_sealed_block(&self, block: SealedBlock) -> ImportResult; } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 621f880c5..f51f978de 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -29,9 +29,10 @@ use blockchain::extras::BlockReceipts; use error::{ImportResult}; use evm::Factory as EvmFactory; use miner::{Miner, MinerService}; +use spec::Spec; use block_queue::BlockQueueInfo; -use block::OpenBlock; +use block::{OpenBlock, SealedBlock}; use executive::Executed; use error::{ExecutionError}; use trace::LocalizedTrace; @@ -105,7 +106,7 @@ impl TestBlockChainClient { execution_result: RwLock::new(None), receipts: RwLock::new(HashMap::new()), queue_size: AtomicUsize::new(0), - miner: Arc::new(Miner::default()), + miner: Arc::new(Miner::with_spec(Spec::new_test())), }; client.add_blocks(1, EachBlockWith::Nothing); // add genesis block client.genesis_hash = client.last_hash.read().unwrap().clone(); @@ -243,6 +244,14 @@ impl MiningBlockChainClient for TestBlockChainClient { fn prepare_open_block(&self, _author: Address, _gas_range_target: (U256, U256), _extra_data: Bytes) -> OpenBlock { unimplemented!(); } + + fn vm_factory(&self) -> &EvmFactory { + unimplemented!(); + } + + fn import_sealed_block(&self, _block: SealedBlock) -> ImportResult { + unimplemented!(); + } } impl BlockChainClient for TestBlockChainClient { @@ -462,10 +471,6 @@ impl BlockChainClient for TestBlockChainClient { } } - fn vm_factory(&self) -> &EvmFactory { - unimplemented!(); - } - fn filter_traces(&self, _filter: TraceFilter) -> Option> { unimplemented!(); } @@ -499,7 +504,7 @@ impl BlockChainClient for TestBlockChainClient { self.import_transactions(tx); } - fn all_transactions(&self) -> Vec { - self.miner.all_transactions() + fn pending_transactions(&self) -> Vec { + self.miner.pending_transactions() } } diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 3400220db..84c2a9608 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -14,9 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -extern crate ethash; - -use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256}; +use ethash::{quick_get_difficulty, EthashManager, H256 as EH256}; use common::*; use block::*; use spec::CommonParams; diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index 2bee5eb2a..c8e967d76 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -22,6 +22,7 @@ use tests::helpers::*; use devtools::*; use spec::Genesis; use ethjson; +use ethjson::blockchain::BlockChain; use miner::Miner; pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { @@ -41,20 +42,28 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { flush!(" - {}...", name); - let mut spec = match era { - ChainEra::Frontier => ethereum::new_frontier_test(), - ChainEra::Homestead => ethereum::new_homestead_test(), + let spec = |blockchain: &BlockChain| { + let genesis = Genesis::from(blockchain.genesis()); + let state = From::from(blockchain.pre_state.clone()); + let mut spec = match era { + ChainEra::Frontier => ethereum::new_frontier_test(), + ChainEra::Homestead => ethereum::new_homestead_test(), + }; + spec.set_genesis_state(state); + spec.overwrite_genesis_params(genesis); + assert!(spec.is_state_root_valid()); + spec }; - let genesis = Genesis::from(blockchain.genesis()); - let state = From::from(blockchain.pre_state.clone()); - spec.set_genesis_state(state); - spec.overwrite_genesis_params(genesis); - assert!(spec.is_state_root_valid()); - let temp = RandomTempPath::new(); { - let client = Client::new(ClientConfig::default(), spec, temp.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); + let client = Client::new( + ClientConfig::default(), + spec(&blockchain), + temp.as_path(), + Arc::new(Miner::with_spec(spec(&blockchain))), + IoChannel::disconnected() + ).unwrap(); for b in &blockchain.blocks_rlp() { if Block::is_good(&b) { let _ = client.import_block(b.clone()); diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 9919ec62a..54a944331 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -91,6 +91,8 @@ extern crate ethjson; extern crate bloomchain; #[macro_use] extern crate ethcore_ipc as ipc; extern crate rayon; +extern crate hyper; +extern crate ethash; pub extern crate ethstore; #[cfg(test)] extern crate ethcore_devtools as devtools; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 709a32482..d3e32c797 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -16,8 +16,10 @@ use rayon::prelude::*; use std::sync::atomic::AtomicBool; +use std::time::{Instant, Duration}; use util::*; +use util::Colour::White; use account_provider::AccountProvider; use views::{BlockView, HeaderView}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; @@ -28,6 +30,61 @@ use receipt::{Receipt}; use spec::Spec; use engine::Engine; use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; +use miner::work_notify::WorkPoster; + +/// Different possible definitions for pending transaction set. +#[derive(Debug)] +pub enum PendingSet { + /// Always just the transactions in the queue. These have had only cheap checks. + AlwaysQueue, + /// Always just the transactions in the sealing block. These have had full checks but + /// may be empty if the node is not actively mining or has force_sealing enabled. + AlwaysSealing, + /// Try the sealing block, but if it is not currently sealing, fallback to the queue. + SealingOrElseQueue, +} + +/// Configures the behaviour of the miner. +#[derive(Debug)] +pub struct MinerOptions { + /// URLs to notify when there is new work. + pub new_work_notify: Vec, + /// Force the miner to reseal, even when nobody has asked for work. + pub force_sealing: bool, + /// Reseal on receipt of new external transactions. + pub reseal_on_external_tx: bool, + /// Reseal on receipt of new local transactions. + pub reseal_on_own_tx: bool, + /// Minimum period between transaction-inspired reseals. + pub reseal_min_period: Duration, + /// Maximum amount of gas to bother considering for block insertion. + pub tx_gas_limit: U256, + /// Maximum size of the transaction queue. + pub tx_queue_size: usize, + /// Whether we should fallback to providing all the queue's transactions or just pending. + pub pending_set: PendingSet, + /// How many historical work packages can we store before running out? + pub work_queue_size: usize, + /// Can we submit two different solutions for the same block and expect both to result in an import? + pub enable_resubmission: bool, +} + +impl Default for MinerOptions { + fn default() -> Self { + MinerOptions { + new_work_notify: vec![], + force_sealing: false, + reseal_on_external_tx: true, + reseal_on_own_tx: true, + tx_gas_limit: !U256::zero(), + tx_queue_size: 1024, + pending_set: PendingSet::AlwaysQueue, + reseal_min_period: Duration::from_secs(0), + work_queue_size: 20, + enable_resubmission: true, + } + } +} /// Keeps track of transactions using priority queue and holds currently mined block. pub struct Miner { @@ -36,8 +93,9 @@ pub struct Miner { sealing_work: Mutex>, // for sealing... - force_sealing: bool, + options: MinerOptions, sealing_enabled: AtomicBool, + next_allowed_reseal: Mutex, sealing_block_last_request: Mutex, gas_range_target: RwLock<(U256, U256)>, author: RwLock
, @@ -45,55 +103,44 @@ pub struct Miner { spec: Spec, accounts: Option>, -} - -impl Default for Miner { - fn default() -> Miner { - Miner { - transaction_queue: Mutex::new(TransactionQueue::new()), - force_sealing: false, - sealing_enabled: AtomicBool::new(false), - sealing_block_last_request: Mutex::new(0), - sealing_work: Mutex::new(UsingQueue::new(5)), - gas_range_target: RwLock::new((U256::zero(), U256::zero())), - author: RwLock::new(Address::default()), - extra_data: RwLock::new(Vec::new()), - accounts: None, - spec: Spec::new_test(), - } - } + work_poster: Option, } impl Miner { - /// Creates new instance of miner - pub fn new(force_sealing: bool, spec: Spec) -> Arc { - Arc::new(Miner { + /// Creates new instance of miner without accounts, but with given spec. + pub fn with_spec(spec: Spec) -> Miner { + Miner { transaction_queue: Mutex::new(TransactionQueue::new()), - force_sealing: force_sealing, - sealing_enabled: AtomicBool::new(force_sealing), + options: Default::default(), + sealing_enabled: AtomicBool::new(false), + next_allowed_reseal: Mutex::new(Instant::now()), sealing_block_last_request: Mutex::new(0), - sealing_work: Mutex::new(UsingQueue::new(5)), + sealing_work: Mutex::new(UsingQueue::new(20)), gas_range_target: RwLock::new((U256::zero(), U256::zero())), author: RwLock::new(Address::default()), extra_data: RwLock::new(Vec::new()), accounts: None, spec: spec, - }) + work_poster: None, + } } /// Creates new instance of miner - pub fn with_accounts(force_sealing: bool, spec: Spec, accounts: Arc) -> Arc { + pub fn new(options: MinerOptions, spec: Spec, accounts: Option>) -> Arc { + let work_poster = if !options.new_work_notify.is_empty() { Some(WorkPoster::new(&options.new_work_notify)) } else { None }; Arc::new(Miner { - transaction_queue: Mutex::new(TransactionQueue::new()), - force_sealing: force_sealing, - sealing_enabled: AtomicBool::new(force_sealing), + transaction_queue: Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)), + sealing_enabled: AtomicBool::new(options.force_sealing || !options.new_work_notify.is_empty()), + next_allowed_reseal: Mutex::new(Instant::now()), sealing_block_last_request: Mutex::new(0), - sealing_work: Mutex::new(UsingQueue::new(5)), + sealing_work: Mutex::new(UsingQueue::new(options.work_queue_size)), gas_range_target: RwLock::new((U256::zero(), U256::zero())), author: RwLock::new(Address::default()), extra_data: RwLock::new(Vec::new()), - accounts: Some(accounts), + options: options, + accounts: accounts, spec: spec, + work_poster: work_poster, }) } @@ -101,15 +148,20 @@ impl Miner { self.spec.engine.deref() } + fn forced_sealing(&self) -> bool { + self.options.force_sealing || !self.options.new_work_notify.is_empty() + } + /// Prepares new block for sealing including top transactions from queue. #[cfg_attr(feature="dev", allow(match_same_arms))] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] fn prepare_sealing(&self, chain: &MiningBlockChainClient) { trace!(target: "miner", "prepare_sealing: entering"); - let (transactions, mut open_block) = { + let (transactions, mut open_block, original_work_hash) = { let transactions = {self.transaction_queue.lock().unwrap().top_transactions()}; let mut sealing_work = self.sealing_work.lock().unwrap(); + let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash()); let best_hash = chain.best_block_header().sha3(); /* // check to see if last ClosedBlock in would_seals is actually same parent block. @@ -136,7 +188,7 @@ impl Miner { ) } }; - (transactions, open_block) + (transactions, open_block, last_work_hash) }; let mut invalid_transactions = HashSet::new(); @@ -202,13 +254,27 @@ impl Miner { } } - let mut sealing_work = self.sealing_work.lock().unwrap(); - if sealing_work.peek_last_ref().map_or(true, |pb| pb.block().fields().header.hash() != block.block().fields().header.hash()) { - trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash()); - sealing_work.push(block); + let (work, is_new) = { + let mut sealing_work = self.sealing_work.lock().unwrap(); + let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash()); + trace!(target: "miner", "Checking whether we need to reseal: orig={:?} last={:?}, this={:?}", original_work_hash, last_work_hash, block.block().fields().header.hash()); + let (work, is_new) = if last_work_hash.map_or(true, |h| h != block.block().fields().header.hash()) { + trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash()); + let pow_hash = block.block().fields().header.hash(); + let number = block.block().fields().header.number(); + let difficulty = *block.block().fields().header.difficulty(); + let is_new = original_work_hash.map_or(true, |h| block.block().fields().header.hash() != h); + sealing_work.push(block); + (Some((pow_hash, difficulty, number)), is_new) + } else { + (None, false) + }; + trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash())); + (work, is_new) + }; + if is_new { + work.map(|(pow_hash, difficulty, number)| self.work_poster.as_ref().map(|ref p| p.notify(pow_hash, difficulty, number))); } - - trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash())); } fn update_gas_limit(&self, chain: &MiningBlockChainClient) { @@ -236,6 +302,9 @@ impl Miner { // Return if !have_work } + + /// Are we allowed to do a non-mandatory reseal? + fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock().unwrap() } } const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; @@ -373,6 +442,10 @@ impl MinerService for Miner { self.transaction_queue.lock().unwrap().set_limit(limit) } + fn set_tx_gas_limit(&self, limit: U256) { + self.transaction_queue.lock().unwrap().set_tx_gas_limit(limit) + } + /// Get the author that we will seal blocks as. fn author(&self) -> Address { *self.author.read().unwrap() @@ -402,7 +475,7 @@ impl MinerService for Miner { .map(|tx| transaction_queue.add(tx, &fetch_account, TransactionOrigin::External)) .collect() }; - if !results.is_empty() { + if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() { self.update_sealing(chain); } results @@ -437,7 +510,7 @@ impl MinerService for Miner { import }; - if imported.is_ok() { + if imported.is_ok() && self.options.reseal_on_own_tx && self.tx_reseal_allowed() { // Make sure to do it after transaction is imported and lock is droped. // We need to create pending block and enable sealing let prepared = self.enable_and_prepare_sealing(chain); @@ -451,26 +524,6 @@ impl MinerService for Miner { imported } - fn pending_transactions_hashes(&self) -> Vec { - let queue = self.transaction_queue.lock().unwrap(); - match (self.sealing_enabled.load(atomic::Ordering::Relaxed), self.sealing_work.lock().unwrap().peek_last_ref()) { - (true, Some(pending)) => pending.transactions().iter().map(|t| t.hash()).collect(), - _ => { - queue.pending_hashes() - } - } - } - - fn transaction(&self, hash: &H256) -> Option { - let queue = self.transaction_queue.lock().unwrap(); - match (self.sealing_enabled.load(atomic::Ordering::Relaxed), self.sealing_work.lock().unwrap().peek_last_ref()) { - (true, Some(pending)) => pending.transactions().iter().find(|t| &t.hash() == hash).cloned(), - _ => { - queue.find(hash) - } - } - } - fn all_transactions(&self) -> Vec { let queue = self.transaction_queue.lock().unwrap(); queue.top_transactions() @@ -478,12 +531,41 @@ impl MinerService for Miner { fn pending_transactions(&self) -> Vec { let queue = self.transaction_queue.lock().unwrap(); + let sw = self.sealing_work.lock().unwrap(); // TODO: should only use the sealing_work when it's current (it could be an old block) - match (self.sealing_enabled.load(atomic::Ordering::Relaxed), self.sealing_work.lock().unwrap().peek_last_ref()) { - (true, Some(pending)) => pending.transactions().clone(), - _ => { - queue.top_transactions() - } + let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) { + true => sw.peek_last_ref(), + false => None, + }; + match (&self.options.pending_set, sealing_set) { + (&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.top_transactions(), + (_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().clone()), + } + } + + fn pending_transactions_hashes(&self) -> Vec { + let queue = self.transaction_queue.lock().unwrap(); + let sw = self.sealing_work.lock().unwrap(); + let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) { + true => sw.peek_last_ref(), + false => None, + }; + match (&self.options.pending_set, sealing_set) { + (&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.pending_hashes(), + (_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().iter().map(|t| t.hash()).collect()), + } + } + + fn transaction(&self, hash: &H256) -> Option { + let queue = self.transaction_queue.lock().unwrap(); + let sw = self.sealing_work.lock().unwrap(); + let sealing_set = match self.sealing_enabled.load(atomic::Ordering::Relaxed) { + true => sw.peek_last_ref(), + false => None, + }; + match (&self.options.pending_set, sealing_set) { + (&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.find(hash), + (_, sealing) => sealing.and_then(|s| s.transactions().iter().find(|t| &t.hash() == hash).cloned()), } } @@ -511,7 +593,7 @@ impl MinerService for Miner { let current_no = chain.chain_info().best_block_number; let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions(); let last_request = *self.sealing_block_last_request.lock().unwrap(); - let should_disable_sealing = !self.force_sealing + let should_disable_sealing = !self.forced_sealing() && !has_local_transactions && current_no > last_request && current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS; @@ -521,6 +603,7 @@ impl MinerService for Miner { self.sealing_enabled.store(false, atomic::Ordering::Relaxed); self.sealing_work.lock().unwrap().reset(); } else { + *self.next_allowed_reseal.lock().unwrap() = Instant::now() + self.options.reseal_min_period; self.prepare_sealing(chain); } } @@ -537,26 +620,22 @@ impl MinerService for Miner { } fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error> { - if let Some(b) = self.sealing_work.lock().unwrap().take_used_if(|b| &b.hash() == &pow_hash) { - match b.lock().try_seal(self.engine(), seal) { - Err(_) => { - info!(target: "miner", "Mined block rejected, PoW was invalid."); - Err(Error::PowInvalid) - } - Ok(sealed) => { - info!(target: "miner", "New block mined, hash: {}", sealed.header().hash()); - // TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice. - let b = sealed.rlp_bytes(); - let h = b.sha3(); - try!(chain.import_block(b)); - info!("Block {} submitted and imported.", h); - Ok(()) - } - } + let result = if let Some(b) = self.sealing_work.lock().unwrap().get_used_if(if self.options.enable_resubmission { GetAction::Clone } else { GetAction::Take }, |b| &b.hash() == &pow_hash) { + b.lock().try_seal(self.engine(), seal).or_else(|_| { + warn!(target: "miner", "Mined solution rejected: Invalid."); + Err(Error::PowInvalid) + }) } else { - info!(target: "miner", "Mined block rejected, PoW hash invalid or out of date."); + warn!(target: "miner", "Mined solution rejected: Block unknown or out of date."); Err(Error::PowHashInvalid) - } + }; + result.and_then(|sealed| { + let n = sealed.header().number(); + let h = sealed.header().hash(); + try!(chain.import_sealed_block(sealed)); + info!(target: "miner", "Mined block imported OK. #{}: {}", paint(White.bold(), format!("{}", n)), paint(White.bold(), h.hex())); + Ok(()) + }) } fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { @@ -626,6 +705,7 @@ mod tests { use util::*; use client::{TestBlockChainClient, EachBlockWith}; use block::*; + use spec::Spec; // TODO [ToDr] To uncomment` when TestBlockChainClient can actually return a ClosedBlock. #[ignore] @@ -633,7 +713,7 @@ mod tests { fn should_prepare_block_to_seal() { // given let client = TestBlockChainClient::default(); - let miner = Miner::default(); + let miner = Miner::with_spec(Spec::new_test()); // when let sealing_work = miner.map_sealing_work(&client, |_| ()); @@ -645,7 +725,7 @@ mod tests { fn should_still_work_after_a_couple_of_blocks() { // given let client = TestBlockChainClient::default(); - let miner = Miner::default(); + let miner = Miner::with_spec(Spec::new_test()); let res = miner.map_sealing_work(&client, |b| b.block().fields().header.hash()); assert!(res.is_some()); diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index b28dcbe8d..152bd1a61 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -28,11 +28,12 @@ //! extern crate ethcore; //! use std::env; //! use util::network::{NetworkService, NetworkConfiguration}; +//! use ethcore::ethereum; //! use ethcore::client::{Client, ClientConfig}; //! use ethcore::miner::{Miner, MinerService}; //! //! fn main() { -//! let miner: Miner = Miner::default(); +//! let miner: Miner = Miner::with_spec(ethereum::new_frontier(true)); //! // get status //! assert_eq!(miner.status().transactions_in_pending_queue, 0); //! @@ -44,9 +45,10 @@ mod miner; mod external; mod transaction_queue; +mod work_notify; pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; -pub use self::miner::{Miner}; +pub use self::miner::{Miner, MinerOptions, PendingSet}; pub use self::external::{ExternalMiner, ExternalMinerService}; use std::collections::BTreeMap; @@ -100,6 +102,9 @@ pub trait MinerService : Send + Sync { /// Set maximal number of transactions kept in the queue (both current and future). fn set_transactions_limit(&self, limit: usize); + /// Set maximum amount of gas allowed for any single transaction to mine. + fn set_tx_gas_limit(&self, limit: U256); + /// Imports transactions to transaction queue. fn import_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec, fetch_account: T) -> Vec> diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index b2ca31e0d..7f5b59c38 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -334,6 +334,8 @@ const GAS_LIMIT_HYSTERESIS: usize = 10; // % pub struct TransactionQueue { /// Gas Price threshold for transactions that can be imported to this queue (defaults to 0) minimal_gas_price: U256, + /// The maximum amount of gas any individual transaction may use. + tx_gas_limit: U256, /// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0) gas_limit: U256, /// Priority queue for transactions that can go to block @@ -355,11 +357,11 @@ impl Default for TransactionQueue { impl TransactionQueue { /// Creates new instance of this Queue pub fn new() -> Self { - Self::with_limit(1024) + Self::with_limits(1024, !U256::zero()) } /// Create new instance of this Queue with specified limits - pub fn with_limit(limit: usize) -> Self { + pub fn with_limits(limit: usize, tx_gas_limit: U256) -> Self { let current = TransactionSet { by_priority: BTreeSet::new(), by_address: Table::new(), @@ -374,6 +376,7 @@ impl TransactionQueue { TransactionQueue { minimal_gas_price: U256::zero(), + tx_gas_limit: tx_gas_limit, gas_limit: !U256::zero(), current: current, future: future, @@ -418,6 +421,12 @@ impl TransactionQueue { }; } + /// Set the new limit for the amount of gas any individual transaction may have. + /// Any transaction already imported to the queue is not affected. + pub fn set_tx_gas_limit(&mut self, limit: U256) { + self.tx_gas_limit = limit; + } + /// Returns current status for this queue pub fn status(&self) -> TransactionQueueStatus { TransactionQueueStatus { @@ -432,10 +441,12 @@ impl TransactionQueue { trace!(target: "miner", "Importing: {:?}", tx.hash()); - if tx.gas_price < self.minimal_gas_price { + if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local { trace!(target: "miner", "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", - tx.hash(), tx.gas_price, self.minimal_gas_price + tx.hash(), + tx.gas_price, + self.minimal_gas_price ); return Err(Error::Transaction(TransactionError::InsufficientGasPrice { @@ -446,10 +457,13 @@ impl TransactionQueue { try!(tx.check_low_s()); - if tx.gas > self.gas_limit { + if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit { trace!(target: "miner", - "Dropping transaction above gas limit: {:?} ({} > {})", - tx.hash(), tx.gas, self.gas_limit + "Dropping transaction above gas limit: {:?} ({} > min({}, {}))", + tx.hash(), + tx.gas, + self.gas_limit, + self.tx_gas_limit ); return Err(Error::Transaction(TransactionError::GasLimitExceeded { @@ -463,8 +477,13 @@ impl TransactionQueue { let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas; if client_account.balance < cost { - trace!(target: "miner", "Dropping transaction without sufficient balance: {:?} ({} < {})", - vtx.hash(), client_account.balance, cost); + trace!(target: "miner", + "Dropping transaction without sufficient balance: {:?} ({} < {})", + vtx.hash(), + client_account.balance, + cost + ); + return Err(Error::Transaction(TransactionError::InsufficientBalance { cost: cost, balance: client_account.balance @@ -1036,7 +1055,7 @@ mod test { } #[test] - fn should_not_import_transaction_below_min_gas_price_threshold() { + fn should_not_import_transaction_below_min_gas_price_threshold_if_external() { // given let mut txq = TransactionQueue::new(); let tx = new_tx(); @@ -1055,6 +1074,23 @@ mod test { assert_eq!(stats.future, 0); } + #[test] + fn should_import_transaction_below_min_gas_price_threshold_if_local() { + // given + let mut txq = TransactionQueue::new(); + let tx = new_tx(); + txq.set_minimal_gas_price(tx.gas_price + U256::one()); + + // when + let res = txq.add(tx, &default_nonce, TransactionOrigin::Local); + + // then + assert_eq!(res.unwrap(), TransactionImportResult::Current); + let stats = txq.status(); + assert_eq!(stats.pending, 1); + assert_eq!(stats.future, 0); + } + #[test] fn should_reject_incorectly_signed_transaction() { // given @@ -1288,7 +1324,7 @@ mod test { #[test] fn should_drop_old_transactions_when_hitting_the_limit() { // given - let mut txq = TransactionQueue::with_limit(1); + let mut txq = TransactionQueue::with_limits(1, !U256::zero()); let (tx, tx2) = new_txs(U256::one()); let sender = tx.sender().unwrap(); let nonce = tx.nonce; @@ -1310,7 +1346,7 @@ mod test { #[test] fn should_return_correct_nonces_when_dropped_because_of_limit() { // given - let mut txq = TransactionQueue::with_limit(2); + let mut txq = TransactionQueue::with_limits(2, !U256::zero()); let tx = new_tx(); let (tx1, tx2) = new_txs(U256::one()); let sender = tx1.sender().unwrap(); @@ -1331,7 +1367,7 @@ mod test { #[test] fn should_limit_future_transactions() { - let mut txq = TransactionQueue::with_limit(1); + let mut txq = TransactionQueue::with_limits(1, !U256::zero()); txq.current.set_limit(10); let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(4), U256::from(1)); let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(4), U256::from(2)); @@ -1591,7 +1627,7 @@ mod test { #[test] fn should_keep_right_order_in_future() { // given - let mut txq = TransactionQueue::with_limit(1); + let mut txq = TransactionQueue::with_limits(1, !U256::zero()); let (tx1, tx2) = new_txs(U256::from(1)); let prev_nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce - U256::one(), balance: default_nonce(a).balance }; diff --git a/ethcore/src/miner/work_notify.rs b/ethcore/src/miner/work_notify.rs new file mode 100644 index 000000000..a153be79f --- /dev/null +++ b/ethcore/src/miner/work_notify.rs @@ -0,0 +1,115 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +extern crate hyper; + +use hyper::header::ContentType; +use hyper::method::Method; +use hyper::client::{Request, Response, Client}; +use hyper::{Next}; +use hyper::net::HttpStream; +use ethash::SeedHashCompute; +use hyper::Url; +use util::*; +use ethereum::ethash::Ethash; + +pub struct WorkPoster { + urls: Vec, + client: Mutex>, + seed_compute: Mutex, +} + +impl WorkPoster { + pub fn new(urls: &[String]) -> Self { + let urls = urls.into_iter().filter_map(|u| { + match Url::parse(&u) { + Ok(url) => Some(url), + Err(e) => { + warn!("Error parsing URL {} : {}", u, e); + None + } + } + }).collect(); + let client = WorkPoster::create_client(); + WorkPoster { + client: Mutex::new(client), + urls: urls, + seed_compute: Mutex::new(SeedHashCompute::new()), + } + } + + fn create_client() -> Client { + let client = Client::::configure() + .keep_alive(true) + .build().expect("Error creating HTTP client") as Client; + client + } + + pub fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) { + // TODO: move this to engine + let target = Ethash::difficulty_to_boundary(&difficulty); + let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(number); + let seed_hash = H256::from_slice(&seed_hash[..]); + let body = format!(r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#, + pow_hash.hex(), seed_hash.hex(), target.hex(), number); + let mut client = self.client.lock().unwrap(); + for u in &self.urls { + if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) { + warn!("Error sending HTTP notification to {} : {}, retrying", u, e); + // TODO: remove this once https://github.com/hyperium/hyper/issues/848 is fixed + *client = WorkPoster::create_client(); + if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) { + warn!("Error sending HTTP notification to {} : {}", u, e); + } + } + } + } +} + +struct PostHandler { + body: String, +} + +impl hyper::client::Handler for PostHandler { + fn on_request(&mut self, request: &mut Request) -> Next { + request.set_method(Method::Post); + request.headers_mut().set(ContentType::json()); + Next::write() + } + + fn on_request_writable(&mut self, encoder: &mut hyper::Encoder) -> Next { + if let Err(e) = encoder.write_all(self.body.as_bytes()) { + trace!("Error posting work data: {}", e); + } + encoder.close(); + Next::read() + + } + + fn on_response(&mut self, _response: Response) -> Next { + Next::end() + } + + fn on_response_readable(&mut self, _decoder: &mut hyper::Decoder) -> Next { + Next::end() + } + + fn on_error(&mut self, err: hyper::Error) -> Next { + trace!("Error posting work data: {}", err); + Next::end() + } +} + diff --git a/ethcore/src/service.rs b/ethcore/src/service.rs index 03a85ce13..c4cbc497b 100644 --- a/ethcore/src/service.rs +++ b/ethcore/src/service.rs @@ -17,6 +17,7 @@ //! Creates and registers client and network services. use util::*; +use util::Colour::{Yellow, White}; use util::panics::*; use spec::Spec; use error::*; @@ -36,6 +37,8 @@ pub enum SyncMessage { retracted: Vec, /// Hashes of blocks that are now included in cannonical chain enacted: Vec, + /// Hashes of blocks that are sealed by this node + sealed: Vec, }, /// Best Block Hash in chain has been changed NewChainHead, @@ -69,8 +72,7 @@ impl ClientService { try!(net_service.start()); } - info!("Starting {}", net_service.host_info()); - info!("Configured for {} using {:?} engine", spec.name, spec.engine.name()); + info!("Configured for {} using {} engine", paint(White.bold(), spec.name.clone()), paint(Yellow.bold(), spec.engine.name().to_owned())); let client = try!(Client::new(config, spec, db_path, miner, net_service.io().channel())); panic_handler.forward_from(client.deref()); let client_io = Arc::new(ClientIoHandler { @@ -159,9 +161,15 @@ mod tests { #[test] fn it_can_be_started() { - let spec = get_test_spec(); let temp_path = RandomTempPath::new(); - let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path(), Arc::new(Miner::default()), false); + let service = ClientService::start( + ClientConfig::default(), + get_test_spec(), + NetworkConfiguration::new_local(), + &temp_path.as_path(), + Arc::new(Miner::with_spec(get_test_spec())), + false + ); assert!(service.is_ok()); } } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index e7981abba..0c086ffc3 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -208,12 +208,17 @@ impl State { self.require(a, false).set_storage(key, value) } - /// Initialise the code of account `a` so that it is `value` for `key`. + /// Initialise the code of account `a` so that it is `code`. /// NOTE: Account should have been created with `new_contract`. pub fn init_code(&mut self, a: &Address, code: Bytes) { self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).init_code(code); } + /// Reset the code of account `a` so that it is `code`. + pub fn reset_code(&mut self, a: &Address, code: Bytes) { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).reset_code(code); + } + /// Execute a given transaction. /// This will change the state accordingly. pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 79ffa7147..63302cb30 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -24,7 +24,7 @@ use miner::Miner; #[test] fn imports_from_empty() { let dir = RandomTempPath::new(); - let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); + let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap(); client.import_verified_blocks(&IoChannel::disconnected()); client.flush_queue(); } @@ -42,7 +42,7 @@ fn returns_state_root_basic() { #[test] fn imports_good_block() { let dir = RandomTempPath::new(); - let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); + let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap(); let good_block = get_good_dummy_block(); if let Err(_) = client.import_block(good_block) { panic!("error importing block being good by definition"); @@ -57,7 +57,7 @@ fn imports_good_block() { #[test] fn query_none_block() { let dir = RandomTempPath::new(); - let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); + let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap(); let non_existant = client.block_header(BlockID::Number(188)); assert!(non_existant.is_none()); diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index ab538922c..70a644896 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -17,7 +17,7 @@ use client::{BlockChainClient, Client, ClientConfig}; use common::*; use spec::*; -use block::{OpenBlock}; +use block::{OpenBlock, Drain}; use blockchain::{BlockChain, Config as BlockChainConfig}; use state::*; use evm::Schedule; @@ -151,7 +151,7 @@ pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_numbe let dir = RandomTempPath::new(); let test_spec = get_test_spec(); - let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); + let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap(); let test_engine = &test_spec.engine; let mut db_result = get_temp_journal_db(); @@ -250,7 +250,7 @@ pub fn push_blocks_to_client(client: &Arc, timestamp_salt: u64, starting pub fn get_test_client_with_blocks(blocks: Vec) -> GuardedTempResult> { let dir = RandomTempPath::new(); - let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); + let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::with_spec(get_test_spec())), IoChannel::disconnected()).unwrap(); for block in &blocks { if let Err(_) = client.import_block(block.clone()) { panic!("panic importing block which is well-formed"); @@ -303,7 +303,7 @@ pub fn generate_dummy_empty_blockchain() -> GuardedTempResult { pub fn get_temp_journal_db() -> GuardedTempResult> { let temp = RandomTempPath::new(); - let journal_db = journaldb::new(temp.as_str(), journaldb::Algorithm::EarlyMerge, None); + let journal_db = journaldb::new(temp.as_str(), journaldb::Algorithm::EarlyMerge, DatabaseConfig::default()); GuardedTempResult { _temp: temp, result: Some(journal_db) @@ -320,7 +320,7 @@ pub fn get_temp_state() -> GuardedTempResult { } pub fn get_temp_journal_db_in(path: &Path) -> Box { - journaldb::new(path.to_str().unwrap(), journaldb::Algorithm::EarlyMerge, None) + journaldb::new(path.to_str().unwrap(), journaldb::Algorithm::EarlyMerge, DatabaseConfig::default()) } pub fn get_temp_state_in(path: &Path) -> State { diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs index 30e368f1b..e0ebf1b7c 100644 --- a/ethcore/src/verification/canon_verifier.rs +++ b/ethcore/src/verification/canon_verifier.rs @@ -24,11 +24,11 @@ use super::verification; pub struct CanonVerifier; impl Verifier for CanonVerifier { - fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> { + fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> { verification::verify_block_family(header, bytes, engine, bc) } - fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> { + fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { verification::verify_block_final(expected, got) } } diff --git a/ethcore/src/verification/mod.rs b/ethcore/src/verification/mod.rs index fe1f406cc..10aee21f4 100644 --- a/ethcore/src/verification/mod.rs +++ b/ethcore/src/verification/mod.rs @@ -17,11 +17,32 @@ pub mod verification; pub mod verifier; mod canon_verifier; -#[cfg(test)] mod noop_verifier; pub use self::verification::*; pub use self::verifier::Verifier; pub use self::canon_verifier::CanonVerifier; -#[cfg(test)] pub use self::noop_verifier::NoopVerifier; + +/// Verifier type. +#[derive(Debug)] +pub enum VerifierType { + /// Verifies block normally. + Canon, + /// Does not verify block at all. + /// Used in tests. + Noop, +} + +impl Default for VerifierType { + fn default() -> Self { + VerifierType::Canon + } +} + +pub fn new(v: VerifierType) -> Box { + match v { + VerifierType::Canon => Box::new(CanonVerifier), + VerifierType::Noop => Box::new(NoopVerifier), + } +} diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs index 20c15c3f1..99d1d594c 100644 --- a/ethcore/src/verification/noop_verifier.rs +++ b/ethcore/src/verification/noop_verifier.rs @@ -24,11 +24,11 @@ use super::Verifier; pub struct NoopVerifier; impl Verifier for NoopVerifier { - fn verify_block_family(_header: &Header, _bytes: &[u8], _engine: &Engine, _bc: &BlockProvider) -> Result<(), Error> { + fn verify_block_family(&self, _header: &Header, _bytes: &[u8], _engine: &Engine, _bc: &BlockProvider) -> Result<(), Error> { Ok(()) } - fn verify_block_final(_expected: &Header, _got: &Header) -> Result<(), Error> { + fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> { Ok(()) } } diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs index cc5edce29..5db81a4eb 100644 --- a/ethcore/src/verification/verifier.rs +++ b/ethcore/src/verification/verifier.rs @@ -21,6 +21,6 @@ use header::Header; /// Should be used to verify blocks. pub trait Verifier: Send + Sync { - fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>; - fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error>; + fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>; + fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; } diff --git a/ethcore/src/views.rs b/ethcore/src/views.rs deleted file mode 100644 index 0802e11bf..000000000 --- a/ethcore/src/views.rs +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! Block oriented views onto rlp. -use util::*; -use header::*; -use transaction::*; - -/// View onto transaction rlp. -pub struct TransactionView<'a> { - rlp: Rlp<'a> -} - -impl<'a> TransactionView<'a> { - /// Creates new view onto block from raw bytes. - pub fn new(bytes: &'a [u8]) -> TransactionView<'a> { - TransactionView { - rlp: Rlp::new(bytes) - } - } - - /// Creates new view onto block from rlp. - pub fn new_from_rlp(rlp: Rlp<'a>) -> TransactionView<'a> { - TransactionView { - rlp: rlp - } - } - - /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &Rlp<'a> { - &self.rlp - } - - /// Get the nonce field of the transaction. - pub fn nonce(&self) -> U256 { self.rlp.val_at(0) } - - /// Get the gas_price field of the transaction. - pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) } - - /// Get the gas field of the transaction. - pub fn gas(&self) -> U256 { self.rlp.val_at(2) } - - /// Get the value field of the transaction. - pub fn value(&self) -> U256 { self.rlp.val_at(4) } - - /// Get the data field of the transaction. - pub fn data(&self) -> Bytes { self.rlp.val_at(5) } - - /// Get the v field of the transaction. - pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 } - - /// Get the r field of the transaction. - pub fn r(&self) -> U256 { self.rlp.val_at(7) } - - /// Get the s field of the transaction. - pub fn s(&self) -> U256 { self.rlp.val_at(8) } - - // TODO: something like pub fn action(&self) -> Action { self.rlp.val_at(3) } -} - -impl<'a> Hashable for TransactionView<'a> { - fn sha3(&self) -> H256 { - self.rlp.as_raw().sha3() - } -} - -/// View onto transaction rlp. -pub struct AccountView<'a> { - rlp: Rlp<'a> -} - -impl<'a> AccountView<'a> { - /// Creates new view onto block from raw bytes. - pub fn new(bytes: &'a [u8]) -> AccountView<'a> { - AccountView { - rlp: Rlp::new(bytes) - } - } - - /// Creates new view onto block from rlp. - pub fn new_from_rlp(rlp: Rlp<'a>) -> AccountView<'a> { - AccountView { - rlp: rlp - } - } - - /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &Rlp<'a> { - &self.rlp - } - - /// Get the nonce field of the transaction. - pub fn nonce(&self) -> U256 { self.rlp.val_at(0) } - - /// Get the gas_price field of the transaction. - pub fn balance(&self) -> U256 { self.rlp.val_at(1) } - - /// Get the gas field of the transaction. - pub fn storage_root(&self) -> H256 { self.rlp.val_at(2) } - - /// Get the value field of the transaction. - pub fn code_hash(&self) -> H256 { self.rlp.val_at(3) } -} - -/// View onto block rlp. -pub struct BlockView<'a> { - rlp: Rlp<'a> -} - -impl<'a> BlockView<'a> { - /// Creates new view onto block from raw bytes. - pub fn new(bytes: &'a [u8]) -> BlockView<'a> { - BlockView { - rlp: Rlp::new(bytes) - } - } - - /// Creates new view onto block from rlp. - pub fn new_from_rlp(rlp: Rlp<'a>) -> BlockView<'a> { - BlockView { - rlp: rlp - } - } - - /// Return reference to underlaying rlp. - pub fn rlp(&self) -> &Rlp<'a> { - &self.rlp - } - - /// Create new Header object from header rlp. - pub fn header(&self) -> Header { - self.rlp.val_at(0) - } - - /// Create new header view obto block head rlp. - pub fn header_view(&self) -> HeaderView<'a> { - HeaderView::new_from_rlp(self.rlp.at(0)) - } - - /// Return List of transactions in given block. - pub fn transactions(&self) -> Vec { - self.rlp.val_at(1) - } - - /// Return List of transactions with additional localization info. - pub fn localized_transactions(&self) -> Vec { - let header = self.header_view(); - let block_hash = header.sha3(); - let block_number = header.number(); - self.transactions() - .into_iter() - .enumerate() - .map(|(i, t)| LocalizedTransaction { - signed: t, - block_hash: block_hash.clone(), - block_number: block_number, - transaction_index: i - }).collect() - } - - /// Return number of transactions in given block, without deserializing them. - pub fn transactions_count(&self) -> usize { - self.rlp.at(1).iter().count() - } - - /// Return List of transactions in given block. - pub fn transaction_views(&self) -> Vec { - self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect() - } - - /// Return transaction hashes. - pub fn transaction_hashes(&self) -> Vec { - self.rlp.at(1).iter().map(|rlp| rlp.as_raw().sha3()).collect() - } - - /// Returns transaction at given index without deserializing unnecessary data. - pub fn transaction_at(&self, index: usize) -> Option { - self.rlp.at(1).iter().nth(index).map(|rlp| rlp.as_val()) - } - - /// Returns localized transaction at given index. - pub fn localized_transaction_at(&self, index: usize) -> Option { - let header = self.header_view(); - let block_hash = header.sha3(); - let block_number = header.number(); - self.transaction_at(index).map(|t| LocalizedTransaction { - signed: t, - block_hash: block_hash, - block_number: block_number, - transaction_index: index - }) - } - - /// Return list of uncles of given block. - pub fn uncles(&self) -> Vec
{ - self.rlp.val_at(2) - } - - /// Return number of uncles in given block, without deserializing them. - pub fn uncles_count(&self) -> usize { - self.rlp.at(2).iter().count() - } - - /// Return List of transactions in given block. - pub fn uncle_views(&self) -> Vec { - self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect() - } - - /// Return list of uncle hashes of given block. - pub fn uncle_hashes(&self) -> Vec { - self.rlp.at(2).iter().map(|rlp| rlp.as_raw().sha3()).collect() - } - - /// Return nth uncle. - pub fn uncle_at(&self, index: usize) -> Option
{ - self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val()) - } -} - -impl<'a> Hashable for BlockView<'a> { - fn sha3(&self) -> H256 { - self.header_view().sha3() - } -} - -/// View onto block header rlp. -pub struct HeaderView<'a> { - rlp: Rlp<'a> -} - -impl<'a> HeaderView<'a> { - /// Creates new view onto header from raw bytes. - pub fn new(bytes: &'a [u8]) -> HeaderView<'a> { - HeaderView { - rlp: Rlp::new(bytes) - } - } - - /// Creates new view onto header from rlp. - pub fn new_from_rlp(rlp: Rlp<'a>) -> HeaderView<'a> { - HeaderView { - rlp: rlp - } - } - - /// Returns header hash. - pub fn hash(&self) -> H256 { self.sha3() } - - /// Returns raw rlp. - pub fn rlp(&self) -> &Rlp<'a> { &self.rlp } - - /// Returns parent hash. - pub fn parent_hash(&self) -> H256 { self.rlp.val_at(0) } - - /// Returns uncles hash. - pub fn uncles_hash(&self) -> H256 { self.rlp.val_at(1) } - - /// Returns author. - pub fn author(&self) -> Address { self.rlp.val_at(2) } - - /// Returns state root. - pub fn state_root(&self) -> H256 { self.rlp.val_at(3) } - - /// Returns transactions root. - pub fn transactions_root(&self) -> H256 { self.rlp.val_at(4) } - - /// Returns block receipts root. - pub fn receipts_root(&self) -> H256 { self.rlp.val_at(5) } - - /// Returns block log bloom. - pub fn log_bloom(&self) -> H2048 { self.rlp.val_at(6) } - - /// Returns block difficulty. - pub fn difficulty(&self) -> U256 { self.rlp.val_at(7) } - - /// Returns block number. - pub fn number(&self) -> BlockNumber { self.rlp.val_at(8) } - - /// Returns block gas limit. - pub fn gas_limit(&self) -> U256 { self.rlp.val_at(9) } - - /// Returns block gas used. - pub fn gas_used(&self) -> U256 { self.rlp.val_at(10) } - - /// Returns timestamp. - pub fn timestamp(&self) -> u64 { self.rlp.val_at(11) } - - /// Returns block extra data. - pub fn extra_data(&self) -> Bytes { self.rlp.val_at(12) } - - /// Returns a vector of post-RLP-encoded seal fields. - pub fn seal(&self) -> Vec { - let mut seal = vec![]; - for i in 13..self.rlp.item_count() { - seal.push(self.rlp.at(i).as_raw().to_vec()); - } - seal - } -} - -impl<'a> Hashable for HeaderView<'a> { - fn sha3(&self) -> H256 { - self.rlp.as_raw().sha3() - } -} - -#[cfg(test)] -mod tests { - use rustc_serialize::hex::FromHex; - use super::BlockView; - - #[test] - fn test_header_view_seal_fields() { - // that's rlp of block created with ethash engine. - let block_rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); - let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); - let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); - - let block_view = BlockView::new(&block_rlp); - let header_view = block_view.header_view(); - let seal_fields = header_view.seal(); - assert_eq!(seal_fields.len(), 2); - assert_eq!(seal_fields[0], mix_hash); - assert_eq!(seal_fields[1], nonce); - } -} diff --git a/ethcore/src/views/block.rs b/ethcore/src/views/block.rs new file mode 100644 index 000000000..82b8fb805 --- /dev/null +++ b/ethcore/src/views/block.rs @@ -0,0 +1,167 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! View onto block rlp. + +use util::*; +use header::*; +use transaction::*; +use super::{TransactionView, HeaderView}; + +/// View onto block rlp. +pub struct BlockView<'a> { + rlp: Rlp<'a> +} + +impl<'a> BlockView<'a> { + /// Creates new view onto block from raw bytes. + pub fn new(bytes: &'a [u8]) -> BlockView<'a> { + BlockView { + rlp: Rlp::new(bytes) + } + } + + /// Creates new view onto block from rlp. + pub fn new_from_rlp(rlp: Rlp<'a>) -> BlockView<'a> { + BlockView { + rlp: rlp + } + } + + /// Block header hash. + pub fn hash(&self) -> H256 { + self.sha3() + } + + /// Return reference to underlaying rlp. + pub fn rlp(&self) -> &Rlp<'a> { + &self.rlp + } + + /// Create new Header object from header rlp. + pub fn header(&self) -> Header { + self.rlp.val_at(0) + } + + /// Create new header view obto block head rlp. + pub fn header_view(&self) -> HeaderView<'a> { + HeaderView::new_from_rlp(self.rlp.at(0)) + } + + /// Return List of transactions in given block. + pub fn transactions(&self) -> Vec { + self.rlp.val_at(1) + } + + /// Return List of transactions with additional localization info. + pub fn localized_transactions(&self) -> Vec { + let header = self.header_view(); + let block_hash = header.sha3(); + let block_number = header.number(); + self.transactions() + .into_iter() + .enumerate() + .map(|(i, t)| LocalizedTransaction { + signed: t, + block_hash: block_hash.clone(), + block_number: block_number, + transaction_index: i + }).collect() + } + + /// Return number of transactions in given block, without deserializing them. + pub fn transactions_count(&self) -> usize { + self.rlp.at(1).iter().count() + } + + /// Return List of transactions in given block. + pub fn transaction_views(&self) -> Vec { + self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect() + } + + /// Return transaction hashes. + pub fn transaction_hashes(&self) -> Vec { + self.rlp.at(1).iter().map(|rlp| rlp.as_raw().sha3()).collect() + } + + /// Returns transaction at given index without deserializing unnecessary data. + pub fn transaction_at(&self, index: usize) -> Option { + self.rlp.at(1).iter().nth(index).map(|rlp| rlp.as_val()) + } + + /// Returns localized transaction at given index. + pub fn localized_transaction_at(&self, index: usize) -> Option { + let header = self.header_view(); + let block_hash = header.sha3(); + let block_number = header.number(); + self.transaction_at(index).map(|t| LocalizedTransaction { + signed: t, + block_hash: block_hash, + block_number: block_number, + transaction_index: index + }) + } + + /// Return list of uncles of given block. + pub fn uncles(&self) -> Vec
{ + self.rlp.val_at(2) + } + + /// Return number of uncles in given block, without deserializing them. + pub fn uncles_count(&self) -> usize { + self.rlp.at(2).iter().count() + } + + /// Return List of transactions in given block. + pub fn uncle_views(&self) -> Vec { + self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect() + } + + /// Return list of uncle hashes of given block. + pub fn uncle_hashes(&self) -> Vec { + self.rlp.at(2).iter().map(|rlp| rlp.as_raw().sha3()).collect() + } + + /// Return nth uncle. + pub fn uncle_at(&self, index: usize) -> Option
{ + self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val()) + } +} + +impl<'a> Hashable for BlockView<'a> { + fn sha3(&self) -> H256 { + self.header_view().sha3() + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use rustc_serialize::hex::FromHex; + use util::H256; + use super::BlockView; + + #[test] + fn test_block_view() { + // that's rlp of block created with ethash engine. + let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); + + let view = BlockView::new(&rlp); + assert_eq!(view.hash(), H256::from_str("2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259").unwrap()); + assert_eq!(view.transactions_count(), 1); + assert_eq!(view.uncles_count(), 0); + } +} diff --git a/ethcore/src/views/header.rs b/ethcore/src/views/header.rs new file mode 100644 index 000000000..70b59fbfa --- /dev/null +++ b/ethcore/src/views/header.rs @@ -0,0 +1,134 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! View onto block header rlp + +use util::{Rlp, U256, Bytes, Hashable, H256, Address, H2048, View}; +use header::BlockNumber; + +/// View onto block header rlp. +pub struct HeaderView<'a> { + rlp: Rlp<'a> +} + +impl<'a> HeaderView<'a> { + /// Creates new view onto header from raw bytes. + pub fn new(bytes: &'a [u8]) -> HeaderView<'a> { + HeaderView { + rlp: Rlp::new(bytes) + } + } + + /// Creates new view onto header from rlp. + pub fn new_from_rlp(rlp: Rlp<'a>) -> HeaderView<'a> { + HeaderView { + rlp: rlp + } + } + + /// Returns header hash. + pub fn hash(&self) -> H256 { self.sha3() } + + /// Returns raw rlp. + pub fn rlp(&self) -> &Rlp<'a> { &self.rlp } + + /// Returns parent hash. + pub fn parent_hash(&self) -> H256 { self.rlp.val_at(0) } + + /// Returns uncles hash. + pub fn uncles_hash(&self) -> H256 { self.rlp.val_at(1) } + + /// Returns author. + pub fn author(&self) -> Address { self.rlp.val_at(2) } + + /// Returns state root. + pub fn state_root(&self) -> H256 { self.rlp.val_at(3) } + + /// Returns transactions root. + pub fn transactions_root(&self) -> H256 { self.rlp.val_at(4) } + + /// Returns block receipts root. + pub fn receipts_root(&self) -> H256 { self.rlp.val_at(5) } + + /// Returns block log bloom. + pub fn log_bloom(&self) -> H2048 { self.rlp.val_at(6) } + + /// Returns block difficulty. + pub fn difficulty(&self) -> U256 { self.rlp.val_at(7) } + + /// Returns block number. + pub fn number(&self) -> BlockNumber { self.rlp.val_at(8) } + + /// Returns block gas limit. + pub fn gas_limit(&self) -> U256 { self.rlp.val_at(9) } + + /// Returns block gas used. + pub fn gas_used(&self) -> U256 { self.rlp.val_at(10) } + + /// Returns timestamp. + pub fn timestamp(&self) -> u64 { self.rlp.val_at(11) } + + /// Returns block extra data. + pub fn extra_data(&self) -> Bytes { self.rlp.val_at(12) } + + /// Returns a vector of post-RLP-encoded seal fields. + pub fn seal(&self) -> Vec { + let mut seal = vec![]; + for i in 13..self.rlp.item_count() { + seal.push(self.rlp.at(i).as_raw().to_vec()); + } + seal + } +} + +impl<'a> Hashable for HeaderView<'a> { + fn sha3(&self) -> H256 { + self.rlp.as_raw().sha3() + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use rustc_serialize::hex::FromHex; + use util::{H256, Address, H2048, U256}; + use super::HeaderView; + + #[test] + fn test_header_view() { + // that's rlp of block header created with ethash engine. + let rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); + let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); + + let view = HeaderView::new(&rlp); + assert_eq!(view.hash(), H256::from_str("2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259").unwrap()); + assert_eq!(view.parent_hash(), H256::from_str("d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7").unwrap()); + assert_eq!(view.uncles_hash(), H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap()); + assert_eq!(view.author(), Address::from_str("8888f1f195afa192cfee860698584c030f4c9db1").unwrap()); + assert_eq!(view.state_root(), H256::from_str("5fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25").unwrap()); + assert_eq!(view.transactions_root(), H256::from_str("88d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158").unwrap()); + assert_eq!(view.receipts_root(), H256::from_str("07c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1").unwrap()); + assert_eq!(view.log_bloom(), H2048::default()); + assert_eq!(view.difficulty(), U256::from(0x02_00_80)); + assert_eq!(view.number(), 3); + assert_eq!(view.gas_limit(), U256::from(0x2f_ef_ba)); + assert_eq!(view.gas_used(), U256::from(0x52_4d)); + assert_eq!(view.timestamp(), 0x56_8e_93_2a); + assert_eq!(view.extra_data(), vec![] as Vec); + assert_eq!(view.seal(), vec![mix_hash, nonce]); + } +} diff --git a/ethcore/src/views/mod.rs b/ethcore/src/views/mod.rs new file mode 100644 index 000000000..c0102be3d --- /dev/null +++ b/ethcore/src/views/mod.rs @@ -0,0 +1,25 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Block oriented views onto rlp. + +mod block; +mod header; +mod transaction; + +pub use self::block::BlockView; +pub use self::header::HeaderView; +pub use self::transaction::TransactionView; diff --git a/ethcore/src/views/transaction.rs b/ethcore/src/views/transaction.rs new file mode 100644 index 000000000..d83290909 --- /dev/null +++ b/ethcore/src/views/transaction.rs @@ -0,0 +1,97 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! View onto transaction rlp +use util::{Rlp, U256, Bytes, Hashable, H256, View}; + +/// View onto transaction rlp. +pub struct TransactionView<'a> { + rlp: Rlp<'a> +} + +impl<'a> TransactionView<'a> { + /// Creates new view onto block from raw bytes. + pub fn new(bytes: &'a [u8]) -> TransactionView<'a> { + TransactionView { + rlp: Rlp::new(bytes) + } + } + + /// Creates new view onto block from rlp. + pub fn new_from_rlp(rlp: Rlp<'a>) -> TransactionView<'a> { + TransactionView { + rlp: rlp + } + } + + /// Return reference to underlaying rlp. + pub fn rlp(&self) -> &Rlp<'a> { + &self.rlp + } + + /// Get the nonce field of the transaction. + pub fn nonce(&self) -> U256 { self.rlp.val_at(0) } + + /// Get the gas_price field of the transaction. + pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) } + + /// Get the gas field of the transaction. + pub fn gas(&self) -> U256 { self.rlp.val_at(2) } + + /// Get the value field of the transaction. + pub fn value(&self) -> U256 { self.rlp.val_at(4) } + + /// Get the data field of the transaction. + pub fn data(&self) -> Bytes { self.rlp.val_at(5) } + + /// Get the v field of the transaction. + pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 } + + /// Get the r field of the transaction. + pub fn r(&self) -> U256 { self.rlp.val_at(7) } + + /// Get the s field of the transaction. + pub fn s(&self) -> U256 { self.rlp.val_at(8) } +} + +impl<'a> Hashable for TransactionView<'a> { + fn sha3(&self) -> H256 { + self.rlp.as_raw().sha3() + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use rustc_serialize::hex::FromHex; + use util::U256; + use super::TransactionView; + + #[test] + fn test_transaction_view() { + let rlp = "f87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".from_hex().unwrap(); + + let view = TransactionView::new(&rlp); + assert_eq!(view.nonce(), U256::from(0)); + assert_eq!(view.gas_price(), U256::from(1)); + assert_eq!(view.gas(), U256::from(0x61a8)); + assert_eq!(view.value(), U256::from(0xa)); + assert_eq!(view.data(), "0000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); + assert_eq!(view.r(), U256::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353").unwrap()); + assert_eq!(view.s(), U256::from_str("efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); + assert_eq!(view.v(), 0x1b); + } +} diff --git a/install-parity.sh b/install-parity.sh index eadf698db..eae35f764 100755 --- a/install-parity.sh +++ b/install-parity.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -PARITY_DEB_URL=https://github.com/ethcore/parity/releases/download/v1.2.0/parity_linux_1.2.0-0_amd64.deb +PARITY_DEB_URL=https://github.com/ethcore/parity/releases/download/v1.2.1/parity_linux_1.2.1-0_amd64.deb function run_installer() diff --git a/parity/cli.rs b/parity/cli.rs index 29333fe9a..7ebbcb0aa 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -42,14 +42,12 @@ Protocol Options: --keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.parity/keys]. --identity NAME Specify your node's name. - -DAO-Rescue Soft-fork Options: - --help-rescue-dao Does nothing - on by default. - --dont-help-rescue-dao Votes against the DAO-rescue soft-fork, but supports - it if it is triggered anyway. - Equivalent to --gas-floor-target=3141592. - --dogmatic Ignores all DAO-rescue soft-fork behaviour. Even if - it means losing mining rewards. + --fork POLICY Specifies the client's fork policy. POLICY must be + one of: + dogmatic - sticks rigidly to the standard chain. + dao-soft - votes for the DAO-rescue soft-fork. + normal - goes with whatever fork is decided but + votes for none. [default: normal]. Account Options: --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. @@ -133,6 +131,29 @@ Sealing/Mining Options: NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION. --force-sealing Force the node to author new blocks as if it were always sealing/mining. + --reseal-on-txs SET Specify which transactions should force the node + to reseal a block. SET is one of: + none - never reseal on new transactions; + own - reseal only on a new local transaction; + ext - reseal only on a new external transaction; + all - reseal on all new transactions [default: all]. + --reseal-min-period MS Specify the minimum time between reseals from + incoming transactions. MS is time measured in + milliseconds [default: 2000]. + --work-queue-size ITEMS Specify the number of historical work packages + which are kept cached lest a solution is found for + them later. High values take more memory but result + in fewer unusable solutions [default: 20]. + --tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas + a single transaction may have for it to be mined. + --relay-set SET Set of transactions to relay. SET may be: + cheap - Relay any transaction in the queue (this + may include invalid transactions); + strict - Relay only executed transactions (this + guarantees we don't relay invalid transactions, but + means we relay nothing if not mining); + lenient - Same as strict when mining, and cheap + when not [default: cheap]. --usd-per-tx USD Amount of USD to be paid for a basic transaction [default: 0.005]. The minimum gas price is set accordingly. @@ -141,13 +162,19 @@ Sealing/Mining Options: web service in turn and fallback on the last known good value [default: auto]. --gas-floor-target GAS Amount of gas per block to target when sealing a new - block [default: 3141592]. + block [default: 4700000]. --gas-cap GAS A cap on how large we will raise the gas limit per - block due to transaction volume [default: 3141592]. + block due to transaction volume [default: 6283184]. --extra-data STRING Specify a custom extra-data for authored blocks, no more than 32 characters. - --tx-limit LIMIT Limit of transactions kept in the queue (waiting to - be included in next block) [default: 1024]. + --tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting + to be included in next block) [default: 1024]. + --remove-solved Move solved blocks from the work package queue + instead of cloning them. This gives a slightly + faster import speed, but means that extra solutions + submitted for the same work package will go unused. + --notify-work URLS URLs to which work package notifications are pushed. + URLS should be a comma-delimited list of HTTP URLs. Footprint Options: --tracing BOOL Indicates if full transaction tracing should be @@ -170,7 +197,12 @@ Footprint Options: --cache MEGABYTES Set total amount of discretionary memory to use for the entire system, overrides other cache and queue options. - --db-cache-size MB Database cache size. + +Database Options: + --db-cache-size MB Override RocksDB database cache size. + --db-compaction TYPE Database compaction type. TYPE may be one of: + ssd - suitable for SSDs and fast HDDs; + hdd - suitable for slow HDDs [default: ssd]. Import/Export Options: --from BLOCK Export from block BLOCK, which may be an index or @@ -241,8 +273,7 @@ pub struct Args { pub flag_chain: String, pub flag_db_path: String, pub flag_identity: String, - pub flag_dont_help_rescue_dao: bool, - pub flag_dogmatic: bool, + pub flag_fork: String, pub flag_unlock: Option, pub flag_password: Vec, pub flag_cache: Option, @@ -283,13 +314,20 @@ pub struct Args { pub flag_signer_path: String, pub flag_no_token: bool, pub flag_force_sealing: bool, + pub flag_reseal_on_txs: String, + pub flag_reseal_min_period: u64, + pub flag_work_queue_size: usize, + pub flag_remove_solved: bool, + pub flag_tx_gas_limit: Option, + pub flag_relay_set: String, pub flag_author: Option, pub flag_usd_per_tx: String, pub flag_usd_per_eth: String, pub flag_gas_floor_target: String, pub flag_gas_cap: String, pub flag_extra_data: Option, - pub flag_tx_limit: usize, + pub flag_tx_queue_size: usize, + pub flag_notify_work: Option, pub flag_logging: Option, pub flag_version: bool, pub flag_from: String, @@ -323,6 +361,7 @@ pub struct Args { pub flag_ipcpath: Option, pub flag_ipcapi: Option, pub flag_db_cache_size: Option, + pub flag_db_compaction: String, } pub fn print_version() { diff --git a/parity/configuration.rs b/parity/configuration.rs index 43a7fcd3c..fb31bf7ad 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -16,6 +16,7 @@ use std::env; use std::fs::File; +use std::time::Duration; use std::io::{BufRead, BufReader}; use std::net::{SocketAddr, IpAddr}; use std::path::PathBuf; @@ -24,9 +25,11 @@ use docopt::Docopt; use die::*; use util::*; +use util::log::Colour::*; use ethcore::account_provider::AccountProvider; use util::network_settings::NetworkSettings; -use ethcore::client::{append_path, get_db_path, ClientConfig, Switch, VMType}; +use ethcore::client::{append_path, get_db_path, ClientConfig, DatabaseCompactionProfile, Switch, VMType}; +use ethcore::miner::{MinerOptions, PendingSet}; use ethcore::ethereum; use ethcore::spec::Spec; use ethsync::SyncConfig; @@ -44,6 +47,13 @@ pub struct Directories { pub signer: String, } +#[derive(Eq, PartialEq, Debug)] +pub enum Policy { + DaoSoft, + Normal, + Dogmatic, +} + impl Configuration { pub fn parse() -> Self { Configuration { @@ -67,6 +77,45 @@ impl Configuration { self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32 } + fn decode_u256(d: &str, argument: &str) -> U256 { + U256::from_dec_str(d).unwrap_or_else(|_| + U256::from_str(clean_0x(d)).unwrap_or_else(|_| + die!("{}: Invalid numeric value for {}. Must be either a decimal or a hex number.", d, argument) + ) + ) + } + + fn work_notify(&self) -> Vec { + self.args.flag_notify_work.as_ref().map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect()) + } + + pub fn miner_options(&self) -> MinerOptions { + let (own, ext) = match self.args.flag_reseal_on_txs.as_str() { + "none" => (false, false), + "own" => (true, false), + "ext" => (false, true), + "all" => (true, true), + x => die!("{}: Invalid value for --reseal option. Use --help for more information.", x) + }; + MinerOptions { + new_work_notify: self.work_notify(), + force_sealing: self.args.flag_force_sealing, + reseal_on_external_tx: ext, + reseal_on_own_tx: own, + tx_gas_limit: self.args.flag_tx_gas_limit.as_ref().map_or(!U256::zero(), |d| Self::decode_u256(d, "--tx-gas-limit")), + tx_queue_size: self.args.flag_tx_queue_size, + pending_set: match self.args.flag_relay_set.as_str() { + "cheap" => PendingSet::AlwaysQueue, + "strict" => PendingSet::AlwaysSealing, + "lenient" => PendingSet::SealingOrElseQueue, + x => die!("{}: Invalid value for --relay-set option. Use --help for more information.", x) + }, + reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period), + work_queue_size: self.args.flag_work_queue_size, + enable_resubmission: !self.args.flag_remove_solved, + } + } + pub fn author(&self) -> Option
{ self.args.flag_etherbase.as_ref() .or(self.args.flag_author.as_ref()) @@ -75,9 +124,18 @@ impl Configuration { })) } + pub fn policy(&self) -> Policy { + match self.args.flag_fork.as_str() { + "dao-soft" => Policy::DaoSoft, + "normal" => Policy::Normal, + "dogmatic" => Policy::Dogmatic, + x => die!("{}: Invalid value given for --policy option. Use --help for more info.", x) + } + } + pub fn gas_floor_target(&self) -> U256 { - if self.args.flag_dont_help_rescue_dao || self.args.flag_dogmatic { - 4_700_000.into() + if self.policy() == Policy::DaoSoft { + 3_141_592.into() } else { let d = &self.args.flag_gas_floor_target; U256::from_dec_str(d).unwrap_or_else(|_| { @@ -87,8 +145,8 @@ impl Configuration { } pub fn gas_ceil_target(&self) -> U256 { - if self.args.flag_dont_help_rescue_dao || self.args.flag_dogmatic { - 10_000_000.into() + if self.policy() == Policy::DaoSoft { + 3_141_592.into() } else { let d = &self.args.flag_gas_cap; U256::from_dec_str(d).unwrap_or_else(|_| { @@ -124,7 +182,7 @@ impl Configuration { let wei_per_usd: f32 = 1.0e18 / usd_per_eth; let gas_per_tx: f32 = 21000.0; let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; - info!("Using a conversion rate of Ξ1 = US${} ({} wei/gas)", usd_per_eth, wei_per_gas); + info!("Using a conversion rate of Ξ1 = {} ({} wei/gas)", paint(White.bold(), format!("US${}", usd_per_eth)), paint(Yellow.bold(), format!("{}", wei_per_gas))); U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap() } } @@ -140,7 +198,7 @@ impl Configuration { pub fn spec(&self) -> Spec { match self.chain().as_str() { - "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(!self.args.flag_dogmatic), + "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(self.policy() != Policy::Dogmatic), "morden" | "testnet" => ethereum::new_morden(), "olympic" => ethereum::new_olympic(), f => Spec::load(contents(f).unwrap_or_else(|_| { @@ -227,7 +285,7 @@ impl Configuration { let mut latest_era = None; let jdb_types = [journaldb::Algorithm::Archive, journaldb::Algorithm::EarlyMerge, journaldb::Algorithm::OverlayRecent, journaldb::Algorithm::RefCounted]; for i in jdb_types.into_iter() { - let db = journaldb::new(&append_path(&get_db_path(Path::new(&self.path()), *i, spec.genesis_header().hash()), "state"), *i, None); + let db = journaldb::new(&append_path(&get_db_path(Path::new(&self.path()), *i, spec.genesis_header().hash()), "state"), *i, kvdb::DatabaseConfig::default()); trace!(target: "parity", "Looking for best DB: {} at {:?}", i, db.latest_era()); match (latest_era, db.latest_era()) { (Some(best), Some(this)) if best >= this => {} @@ -278,6 +336,13 @@ impl Configuration { // forced state db cache size if provided client_config.db_cache_size = self.args.flag_db_cache_size.and_then(|cs| Some(cs / 4)); + // compaction profile + client_config.db_compaction = match self.args.flag_db_compaction.as_str() { + "ssd" => DatabaseCompactionProfile::Default, + "hdd" => DatabaseCompactionProfile::HDD, + _ => { die!("Invalid compaction profile given (--db-compaction argument), expected hdd/default."); } + }; + if self.args.flag_jitvm { client_config.vm_type = VMType::jit().unwrap_or_else(|| die!("Parity built without jit vm.")) } diff --git a/parity/main.rs b/parity/main.rs index cecb0e0a1..d466987ef 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -80,7 +80,7 @@ use std::thread::sleep; use std::time::Duration; use rustc_serialize::hex::FromHex; use ctrlc::CtrlC; -use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError}; +use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, paint, Colour, version}; use util::panics::{MayPanic, ForwardPanic, PanicHandler}; use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path}; use ethcore::error::{Error, ImportError}; @@ -184,10 +184,12 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) let panic_handler = PanicHandler::new_in_arc(); // Setup logging - let logger = setup_log::setup_log(&conf.args.flag_logging); + let logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color()); // Raise fdlimit unsafe { ::fdlimit::raise_fd_limit(); } + info!("Starting {}", paint(Colour::White.bold(), format!("{}", version()))); + let net_settings = conf.net_settings(&spec); let sync_config = conf.sync_config(&spec); @@ -208,13 +210,13 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) let account_service = Arc::new(conf.account_service()); // Miner - let miner = Miner::with_accounts(conf.args.flag_force_sealing, conf.spec(), account_service.clone()); - miner.set_author(conf.author().unwrap_or(Default::default())); + let miner = Miner::new(conf.miner_options(), conf.spec(), Some(account_service.clone())); + miner.set_author(conf.author().unwrap_or_default()); miner.set_gas_floor_target(conf.gas_floor_target()); miner.set_gas_ceil_target(conf.gas_ceil_target()); miner.set_extra_data(conf.extra_data()); miner.set_minimal_gas_price(conf.gas_price()); - miner.set_transactions_limit(conf.args.flag_tx_limit); + miner.set_transactions_limit(conf.args.flag_tx_queue_size); // Build client let mut service = ClientService::start( @@ -320,6 +322,8 @@ fn execute_export(conf: Configuration) { // Setup panic handler let panic_handler = PanicHandler::new_in_arc(); + // Setup logging + let _logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color()); // Raise fdlimit unsafe { ::fdlimit::raise_fd_limit(); } @@ -341,7 +345,7 @@ fn execute_export(conf: Configuration) { // Build client let service = ClientService::start( - client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()), false + client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::with_spec(conf.spec())), false ).unwrap_or_else(|e| die_with_error("Client", e)); panic_handler.forward_from(&service); @@ -392,6 +396,8 @@ fn execute_import(conf: Configuration) { // Setup panic handler let panic_handler = PanicHandler::new_in_arc(); + // Setup logging + let _logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color()); // Raise fdlimit unsafe { ::fdlimit::raise_fd_limit(); } @@ -413,7 +419,7 @@ fn execute_import(conf: Configuration) { // Build client let service = ClientService::start( - client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()), false + client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::with_spec(conf.spec())), false ).unwrap_or_else(|e| die_with_error("Client", e)); panic_handler.forward_from(&service); diff --git a/parity/migration.rs b/parity/migration.rs index 4198f7ed0..df2c2116f 100644 --- a/parity/migration.rs +++ b/parity/migration.rs @@ -20,7 +20,7 @@ use std::io::{Read, Write, Error as IoError, ErrorKind}; use std::path::PathBuf; use std::fmt::{Display, Formatter, Error as FmtError}; use util::migration::{Manager as MigrationManager, Config as MigrationConfig, MigrationIterator}; -use util::kvdb::{Database, DatabaseConfig}; +use util::kvdb::{Database, DatabaseConfig, CompactionProfile}; use ethcore::migrations; /// Database is assumed to be at default version, when no version file is found. @@ -163,6 +163,7 @@ fn migrate_database(version: u32, path: PathBuf, migrations: MigrationManager) - prefix_size: None, max_open_files: 64, cache_size: None, + compaction: CompactionProfile::default(), }; // open old database diff --git a/parity/setup_log.rs b/parity/setup_log.rs index 4ed153fc2..d347a6bf0 100644 --- a/parity/setup_log.rs +++ b/parity/setup_log.rs @@ -19,10 +19,10 @@ use std::env; use std::sync::Arc; use time; use env_logger::LogBuilder; -use util::{RotatingLogger}; +use util::RotatingLogger; /// Sets up the logger -pub fn setup_log(init: &Option) -> Arc { +pub fn setup_log(init: &Option, enable_color: bool) -> Arc { use rlog::*; let mut levels = String::new(); @@ -43,7 +43,7 @@ pub fn setup_log(init: &Option) -> Arc { builder.parse(s); } - let logs = Arc::new(RotatingLogger::new(levels)); + let logs = Arc::new(RotatingLogger::new(levels, enable_color)); let logger = logs.clone(); let format = move |record: &LogRecord| { let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 9b1f06416..8bfc661e3 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -18,6 +18,8 @@ extern crate ethash; +use std::thread; +use std::time::{Instant, Duration}; use std::sync::{Arc, Weak, Mutex}; use std::ops::Deref; use ethsync::{SyncProvider, SyncState}; @@ -479,6 +481,12 @@ impl Eth for EthClient where trace!(target: "miner", "Syncing. Cannot give any work."); return Err(no_work_err()); } + + // Otherwise spin until our submitted block has been included. + let timeout = Instant::now() + Duration::from_millis(1000); + while Instant::now() < timeout && client.queue_info().total_queue_size() > 0 { + thread::sleep(Duration::from_millis(1)); + } } let miner = take_weak!(self.miner); @@ -490,7 +498,7 @@ impl Eth for EthClient where let pow_hash = b.hash(); let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(b.block().header().number()); - to_value(&(pow_hash, H256::from_slice(&seed_hash[..]), target)) + to_value(&(pow_hash, H256::from_slice(&seed_hash[..]), target, &U256::from(b.block().header().number()))) }).unwrap_or(Err(Error::internal_error())) // no work found. }, _ => Err(Error::invalid_params()) diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index 700c679d6..fa4330f46 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -29,6 +29,9 @@ use v1::impls::{default_gas_price, sign_and_dispatch}; fn fill_optional_fields(request: &mut TransactionRequest, client: &C, miner: &M) where C: MiningBlockChainClient, M: MinerService { + if request.value.is_none() { + request.value = Some(U256::zero()); + } if request.gas.is_none() { request.gas = Some(miner.sensible_gas_limit()); } diff --git a/rpc/src/v1/impls/ethcore_set.rs b/rpc/src/v1/impls/ethcore_set.rs index 55164217c..baf5bf134 100644 --- a/rpc/src/v1/impls/ethcore_set.rs +++ b/rpc/src/v1/impls/ethcore_set.rs @@ -22,7 +22,7 @@ use jsonrpc_core::*; use ethcore::miner::MinerService; use ethcore::service::SyncMessage; use v1::traits::EthcoreSet; -use v1::types::{Bytes}; +use v1::types::Bytes; /// Ethcore-specific rpc interface for operations altering the settings. pub struct EthcoreSetClient where @@ -86,6 +86,13 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { }) } + fn set_tx_gas_limit(&self, params: Params) -> Result { + from_params::<(U256,)>(params).and_then(|(limit,)| { + take_weak!(self.miner).set_tx_gas_limit(limit.into()); + to_value(&true) + }) + } + fn add_reserved_peer(&self, params: Params) -> Result { from_params::<(String,)>(params).and_then(|(peer,)| { match take_weak!(self.net).add_reserved_peer(&peer) { diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index fdd2de8fa..1a6b1c398 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -165,7 +165,7 @@ fn transaction_error(error: EthcoreError) -> Error { "There is too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into() }, InsufficientGasPrice { minimal, got } => { - format!("Transaction fee is to low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got) + format!("Transaction fee is too low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got) }, InsufficientBalance { balance, cost } => { format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance) diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 5cc0176c7..2965a62d2 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -17,6 +17,7 @@ //! rpc integration tests. use std::sync::Arc; use std::str::FromStr; +use std::time::Duration; use ethcore::client::{BlockChainClient, Client, ClientConfig}; use ethcore::ids::BlockID; @@ -24,12 +25,12 @@ use ethcore::spec::{Genesis, Spec}; use ethcore::block::Block; use ethcore::views::BlockView; use ethcore::ethereum; -use ethcore::miner::{MinerService, ExternalMiner, Miner}; +use ethcore::miner::{MinerOptions, MinerService, ExternalMiner, Miner, PendingSet}; use ethcore::account_provider::AccountProvider; use devtools::RandomTempPath; use util::Hashable; use util::io::IoChannel; -use util::{U256, H256}; +use util::{U256, H256, Uint}; use jsonrpc_core::IoHandler; use ethjson::blockchain::BlockChain; @@ -49,7 +50,22 @@ fn sync_provider() -> Arc { } fn miner_service(spec: Spec, accounts: Arc) -> Arc { - Miner::with_accounts(true, spec, accounts) + Miner::new( + MinerOptions { + new_work_notify: vec![], + force_sealing: true, + reseal_on_external_tx: true, + reseal_on_own_tx: true, + tx_queue_size: 1024, + tx_gas_limit: !U256::zero(), + pending_set: PendingSet::SealingOrElseQueue, + reseal_min_period: Duration::from_secs(0), + work_queue_size: 50, + enable_resubmission: true, + }, + spec, + Some(accounts) + ) } fn make_spec(chain: &BlockChain) -> Spec { diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index b87b650e1..6329be7bd 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -43,6 +43,7 @@ pub struct TestMinerService { author: RwLock
, extra_data: RwLock, limit: RwLock, + tx_gas_limit: RwLock, } impl Default for TestMinerService { @@ -58,6 +59,7 @@ impl Default for TestMinerService { author: RwLock::new(Address::zero()), extra_data: RwLock::new(vec![1, 2, 3, 4]), limit: RwLock::new(1024), + tx_gas_limit: RwLock::new(!U256::zero()), } } } @@ -99,6 +101,10 @@ impl MinerService for TestMinerService { *self.limit.write().unwrap() = limit; } + fn set_tx_gas_limit(&self, limit: U256) { + *self.tx_gas_limit.write().unwrap() = limit; + } + fn transactions_limit(&self) -> usize { *self.limit.read().unwrap() } diff --git a/rpc/src/v1/tests/mocked/ethcore.rs b/rpc/src/v1/tests/mocked/ethcore.rs index 5b88e8756..cbdddc2b0 100644 --- a/rpc/src/v1/tests/mocked/ethcore.rs +++ b/rpc/src/v1/tests/mocked/ethcore.rs @@ -32,7 +32,7 @@ fn client_service() -> Arc { } fn logger() -> Arc { - Arc::new(RotatingLogger::new("rpc=trace".to_owned())) + Arc::new(RotatingLogger::new("rpc=trace".to_owned(), false)) } fn settings() -> Arc { diff --git a/rpc/src/v1/traits/ethcore_set.rs b/rpc/src/v1/traits/ethcore_set.rs index d00295036..8afbcdab9 100644 --- a/rpc/src/v1/traits/ethcore_set.rs +++ b/rpc/src/v1/traits/ethcore_set.rs @@ -40,6 +40,9 @@ pub trait EthcoreSet: Sized + Send + Sync + 'static { /// Sets the limits for transaction queue. fn set_transactions_limit(&self, _: Params) -> Result; + /// Sets the maximum amount of gas a single transaction may consume. + fn set_tx_gas_limit(&self, _: Params) -> Result; + /// Add a reserved peer. fn add_reserved_peer(&self, _: Params) -> Result; @@ -60,6 +63,7 @@ pub trait EthcoreSet: Sized + Send + Sync + 'static { delegate.add_method("ethcore_setGasCeilTarget", EthcoreSet::set_gas_ceil_target); delegate.add_method("ethcore_setExtraData", EthcoreSet::set_extra_data); delegate.add_method("ethcore_setAuthor", EthcoreSet::set_author); + delegate.add_method("ethcore_setMaxTransactionGas", EthcoreSet::set_tx_gas_limit); delegate.add_method("ethcore_setTransactionsLimit", EthcoreSet::set_transactions_limit); delegate.add_method("ethcore_addReservedPeer", EthcoreSet::add_reserved_peer); delegate.add_method("ethcore_removeReservedPeer", EthcoreSet::remove_reserved_peer); diff --git a/rpc/src/v1/types/optionals.rs b/rpc/src/v1/types/optionals.rs index 2ed272ade..5f62dc4a0 100644 --- a/rpc/src/v1/types/optionals.rs +++ b/rpc/src/v1/types/optionals.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use serde::{Serialize, Serializer}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; use serde_json::Value; /// Optional value @@ -26,13 +26,22 @@ pub enum OptionalValue where T: Serialize { Null } -impl Default for OptionalValue where T: Serialize { +impl Default for OptionalValue where T: Serialize + Deserialize { fn default() -> Self { OptionalValue::Null } } -impl Serialize for OptionalValue where T: Serialize { +impl Into> for OptionalValue where T: Serialize + Deserialize { + fn into(self) -> Option { + match self { + OptionalValue::Null => None, + OptionalValue::Value(t) => Some(t), + } + } +} + +impl Serialize for OptionalValue where T: Serialize + Deserialize { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { match *self { @@ -42,6 +51,17 @@ impl Serialize for OptionalValue where T: Serialize { } } +impl Deserialize for OptionalValue where T: Serialize + Deserialize { + fn deserialize(deserializer: &mut D) -> Result, D::Error> + where D: Deserializer { + let deser_result: Result = Deserialize::deserialize(deserializer); + match deser_result { + Ok(t) => Ok(OptionalValue::Value(t)), + Err(_) => Ok(OptionalValue::Null), + } + } +} + #[cfg(test)] mod tests { use serde_json; diff --git a/signer/Cargo.toml b/signer/Cargo.toml index e7d6dcd27..4ef8bb03e 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -15,10 +15,10 @@ rand = "0.3.14" jsonrpc-core = "2.0" log = "0.3" env_logger = "0.3" -ws = { git = "https://github.com/ethcore/ws-rs.git" } +ws = { git = "https://github.com/ethcore/ws-rs.git", branch = "stable" } ethcore-util = { path = "../util" } ethcore-rpc = { path = "../rpc" } -parity-minimal-sysui = { git = "https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git" } +parity-minimal-sysui = { git = "https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git", version = "0.2.0" } clippy = { version = "0.0.77", optional = true} diff --git a/signer/src/authcode_store.rs b/signer/src/authcode_store.rs index 92e86a73e..e85633d2c 100644 --- a/signer/src/authcode_store.rs +++ b/signer/src/authcode_store.rs @@ -120,7 +120,7 @@ impl AuthCodes { .filter_map(|f| String::from_utf8(f.to_vec()).ok()) .collect::>() .join("-"); - info!(target: "signer", "New authentication token generated."); + trace!(target: "signer", "New authentication token generated."); self.codes.push(code); Ok(readable_code) } diff --git a/signer/src/ws_server/mod.rs b/signer/src/ws_server/mod.rs index b6b7c15f7..d2d838b2c 100644 --- a/signer/src/ws_server/mod.rs +++ b/signer/src/ws_server/mod.rs @@ -110,7 +110,17 @@ impl Server { // Spawn a thread with event loop let handle = thread::spawn(move || { ph.catch_panic(move || { - ws.listen(addr).unwrap() + match ws.listen(addr).map_err(ServerError::from) { + Err(ServerError::IoError(io)) => die(format!( + "Signer: Could not start listening on specified address. Make sure that no other instance is running on Signer's port. Details: {:?}", + io + )), + Err(any_error) => die(format!( + "Signer: Unknown error occured when starting Signer. Details: {:?}", + any_error + )), + Ok(server) => server, + } }).unwrap() }); @@ -123,7 +133,11 @@ impl Server { // TODO [ToDr] Some better structure here for messages. broadcaster.send("new_message").unwrap(); }).expect("It's the only place we are running start_listening. It shouldn't fail."); - broadcaster.shutdown().expect("Broadcaster should close gently.") + let res = broadcaster.shutdown(); + + if let Err(e) = res { + warn!("Signer: Broadcaster was not closed cleanly. Details: {:?}", e); + } }).unwrap() }); @@ -148,5 +162,11 @@ impl Drop for Server { self.queue.finish(); self.broadcaster_handle.take().unwrap().join().unwrap(); self.handle.take().unwrap().join().unwrap(); + } } + +fn die(msg: String) -> ! { + println!("ERROR: {}", msg); + std::process::exit(1); +} diff --git a/signer/src/ws_server/session.rs b/signer/src/ws_server/session.rs index b635f1524..4b2eb808c 100644 --- a/signer/src/ws_server/session.rs +++ b/signer/src/ws_server/session.rs @@ -62,6 +62,19 @@ fn auth_is_valid(codes: &Path, protocols: ws::Result>) -> bool { } } +fn add_headers(mut response: ws::Response, mime: &str) -> ws::Response { + let content_len = format!("{}", response.len()); + { + let mut headers = response.headers_mut(); + headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec())); + headers.push(("Server".into(), b"Parity/SignerUI".to_vec())); + headers.push(("Content-Length".into(), content_len.as_bytes().to_vec())); + headers.push(("Content-Type".into(), mime.as_bytes().to_vec())); + headers.push(("Connection".into(), b"close".to_vec())); + } + response +} + pub struct Session { out: ws::Sender, self_origin: String, @@ -98,26 +111,13 @@ impl ws::Handler for Session { } // Otherwise try to serve a page. - sysui::handle(req.resource()) + Ok(sysui::handle(req.resource()) .map_or_else( - // return error - || Ok(ws::Response::not_found("Page not found".into())), + // return 404 not found + || add_headers(ws::Response::not_found("Not found".into()), "text/plain"), // or serve the file - |f| { - let content_len = format!("{}", f.content.as_bytes().len()); - let mut res = ws::Response::ok(f.content.into()); - { - let mut headers = res.headers_mut(); - headers.push(("Server".into(), b"Parity/SignerUI".to_vec())); - headers.push(("Connection".into(), b"Closed".to_vec())); - headers.push(("Content-Length".into(), content_len.as_bytes().to_vec())); - headers.push(("Content-Type".into(), f.mime.as_bytes().to_vec())); - if !f.safe_to_embed { - headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec())); - } - } - Ok(res) - }) + |f| add_headers(ws::Response::ok(f.content.into()), &f.mime) + )) } fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 5d0976aac..1897b02f4 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1231,6 +1231,14 @@ impl ChainSync { rlp_stream.out() } + /// creates latest block rlp for the given client + fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes { + let mut rlp_stream = RlpStream::new_list(2); + rlp_stream.append_raw(&chain.block(BlockID::Hash(hash.clone())).expect("Block has just been sealed; qed"), 1); + rlp_stream.append(&chain.block_total_difficulty(BlockID::Hash(hash.clone())).expect("Block has just been sealed; qed.")); + rlp_stream.out() + } + /// returns peer ids that have less blocks than our chain fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &SyncIo) -> Vec<(PeerId, BlockNumber)> { let latest_hash = chain_info.best_block_hash; @@ -1250,7 +1258,6 @@ impl ChainSync { .collect::>() } - fn select_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> Vec<(PeerId, BlockNumber)> { use rand::Rng; let mut lagging_peers = self.get_lagging_peers(chain_info, io); @@ -1263,13 +1270,24 @@ impl ChainSync { } /// propagates latest block to lagging peers - fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> usize { - let lucky_peers = self.select_lagging_peers(chain_info, io); + fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo, sealed: &[H256]) -> usize { + let lucky_peers: Vec<_> = if sealed.is_empty() { + self.select_lagging_peers(chain_info, io).iter().map(|&(id, _)| id).collect() + } else { + self.peers.keys().cloned().collect() + }; trace!(target: "sync", "Sending NewBlocks to {:?}", lucky_peers); let mut sent = 0; - for (peer_id, _) in lucky_peers { - let rlp = ChainSync::create_latest_block_rlp(io.chain()); - self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp); + for peer_id in lucky_peers { + if sealed.is_empty() { + let rlp = ChainSync::create_latest_block_rlp(io.chain()); + self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp); + } else { + for h in sealed { + let rlp = ChainSync::create_new_block_rlp(io.chain(), h); + self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp); + } + } self.peers.get_mut(&peer_id).unwrap().latest_hash = chain_info.best_block_hash.clone(); self.peers.get_mut(&peer_id).unwrap().latest_number = Some(chain_info.best_block_number); sent += 1; @@ -1314,7 +1332,7 @@ impl ChainSync { return 0; } - let mut transactions = io.chain().all_transactions(); + let mut transactions = io.chain().pending_transactions(); if transactions.is_empty() { return 0; } @@ -1346,11 +1364,11 @@ impl ChainSync { sent } - fn propagate_latest_blocks(&mut self, io: &mut SyncIo) { + fn propagate_latest_blocks(&mut self, io: &mut SyncIo, sealed: &[H256]) { let chain_info = io.chain().chain_info(); if (((chain_info.best_block_number as i64) - (self.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION { let hashes = self.propagate_new_hashes(&chain_info, io); - let blocks = self.propagate_blocks(&chain_info, io); + let blocks = self.propagate_blocks(&chain_info, io, sealed); if blocks != 0 || hashes != 0 { trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes); } @@ -1365,10 +1383,10 @@ impl ChainSync { } /// called when block is imported to chain, updates transactions queue and propagates the blocks - pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) { + pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], _enacted: &[H256], _retracted: &[H256], sealed: &[H256]) { if io.is_chain_queue_empty() { // Propagate latests blocks - self.propagate_latest_blocks(io); + self.propagate_latest_blocks(io, sealed); } if !invalid.is_empty() { trace!(target: "sync", "Bad blocks in the queue, restarting"); @@ -1637,7 +1655,26 @@ mod tests { let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let chain_info = client.chain_info(); let mut io = TestIo::new(&mut client, &mut queue, None); - let peer_count = sync.propagate_blocks(&chain_info, &mut io); + let peer_count = sync.propagate_blocks(&chain_info, &mut io, &[]); + + // 1 message should be send + assert_eq!(1, io.queue.len()); + // 1 peer should be updated + assert_eq!(1, peer_count); + // NEW_BLOCK_PACKET + assert_eq!(0x07, io.queue[0].packet_id); + } + + #[test] + fn sends_sealed_block() { + let mut client = TestBlockChainClient::new(); + client.add_blocks(100, EachBlockWith::Uncle); + let mut queue = VecDeque::new(); + let hash = client.block_hash(BlockID::Number(99)).unwrap(); + let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); + let chain_info = client.chain_info(); + let mut io = TestIo::new(&mut client, &mut queue, None); + let peer_count = sync.propagate_blocks(&chain_info, &mut io, &[hash.clone()]); // 1 message should be send assert_eq!(1, io.queue.len()); @@ -1761,7 +1798,7 @@ mod tests { let chain_info = client.chain_info(); let mut io = TestIo::new(&mut client, &mut queue, None); - sync.propagate_blocks(&chain_info, &mut io); + sync.propagate_blocks(&chain_info, &mut io, &[]); let data = &io.queue[0].data.clone(); let result = sync.on_peer_new_block(&mut io, 0, &UntrustedRlp::new(data)); @@ -1794,7 +1831,7 @@ mod tests { let mut queue = VecDeque::new(); let mut io = TestIo::new(&mut client, &mut queue, None); io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks); - sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks); + sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[]); assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0); assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 1); } @@ -1808,7 +1845,7 @@ mod tests { let mut queue = VecDeque::new(); let mut io = TestIo::new(&mut client, &mut queue, None); io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks); - sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks); + sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[]); } // then @@ -1833,10 +1870,10 @@ mod tests { let mut io = TestIo::new(&mut client, &mut queue, None); // when - sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks); + sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[]); assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0); assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 0); - sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks); + sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[]); // then let status = io.chain.miner.status(); diff --git a/sync/src/lib.rs b/sync/src/lib.rs index c3935f5e1..fa26e7d85 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -44,8 +44,14 @@ //! let mut service = NetworkService::new(NetworkConfiguration::new()).unwrap(); //! service.start().unwrap(); //! let dir = env::temp_dir(); -//! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(true), &dir, Arc::new(Miner::default()), service.io().channel()).unwrap(); -//! let miner = Miner::new(false, ethereum::new_frontier(true)); +//! let miner = Miner::new(Default::default(), ethereum::new_frontier(true), None); +//! let client = Client::new( +//! ClientConfig::default(), +//! ethereum::new_frontier(true), +//! &dir, +//! miner, +//! service.io().channel() +//! ).unwrap(); //! let sync = EthSync::new(SyncConfig::default(), client); //! EthSync::register(&mut service, sync); //! } @@ -154,11 +160,13 @@ impl SyncProvider for EthSync { } fn start_network(&self) { - self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StartNetwork)).expect("Error sending IO notification"); + self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StartNetwork)) + .unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e)); } fn stop_network(&self) { - self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StopNetwork)).expect("Error sending IO notification"); + self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StopNetwork)) + .unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e)); } } @@ -188,9 +196,9 @@ impl NetworkProtocolHandler for EthSync { #[cfg_attr(feature="dev", allow(single_match))] fn message(&self, io: &NetworkContext, message: &SyncMessage) { match *message { - SyncMessage::NewChainBlocks { ref imported, ref invalid, ref enacted, ref retracted } => { + SyncMessage::NewChainBlocks { ref imported, ref invalid, ref enacted, ref retracted, ref sealed } => { let mut sync_io = NetSyncIo::new(io, self.chain.deref()); - self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted); + self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted, sealed); }, _ => {/* Ignore other messages */}, } diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index 831976048..9a9afca49 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -173,6 +173,6 @@ impl TestNet { pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) { let mut peer = self.peer_mut(peer_id); - peer.sync.write().unwrap().chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[], &[]); + peer.sync.write().unwrap().chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[], &[], &[]); } } diff --git a/util/Cargo.toml b/util/Cargo.toml index 5cd0a714b..e6f62dd86 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -37,6 +37,7 @@ vergen = "0.1" target_info = "0.1" bigint = { path = "bigint" } chrono = "0.2" +ansi_term = "0.7" [features] default = [] diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index 3e629032e..2b9863135 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -557,7 +557,7 @@ macro_rules! construct_uint { ($name:ident, $n_words:expr) => ( /// Little-endian large integer type #[repr(C)] - #[derive(Copy, Clone, Eq, PartialEq)] + #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct $name(pub [u64; $n_words]); impl Uint for $name { @@ -1126,14 +1126,6 @@ macro_rules! construct_uint { Ok(()) } } - - #[cfg_attr(feature="dev", allow(derive_hash_xor_eq))] // We are pretty sure it's ok. - impl Hash for $name { - fn hash(&self, state: &mut H) where H: Hasher { - unsafe { state.write(::std::slice::from_raw_parts(self.0.as_ptr() as *mut u8, self.0.len() * 8)); } - state.finish(); - } - } ); } diff --git a/util/src/hash.rs b/util/src/hash.rs index 6c1f8b2a4..16f0bde9e 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -132,15 +132,10 @@ macro_rules! impl_hash { $size } - // TODO: remove once slice::clone_from_slice is stable #[inline] fn clone_from_slice(&mut self, src: &[u8]) -> usize { - let min = ::std::cmp::min($size, src.len()); - let dst = &mut self.deref_mut()[.. min]; - let src = &src[.. min]; - for i in 0..min { - dst[i] = src[i]; - } + let min = cmp::min($size, src.len()); + self.0[..min].copy_from_slice(&src[..min]); min } @@ -151,7 +146,7 @@ macro_rules! impl_hash { } fn copy_to(&self, dest: &mut[u8]) { - let min = ::std::cmp::min($size, dest.len()); + let min = cmp::min($size, dest.len()); dest[..min].copy_from_slice(&self.0[..min]); } diff --git a/util/src/io/service.rs b/util/src/io/service.rs index 65be55540..07e60f766 100644 --- a/util/src/io/service.rs +++ b/util/src/io/service.rs @@ -135,8 +135,9 @@ impl IoContext where Message: Send + Clone + 'static { } /// Broadcast a message to other IO clients - pub fn message(&self, message: Message) { - self.channel.send(message).expect("Error seding message"); + pub fn message(&self, message: Message) -> Result<(), UtilError> { + try!(self.channel.send(message)); + Ok(()) } /// Get message channel @@ -351,7 +352,9 @@ impl IoService where Message: Send + Sync + Clone + 'static { /// Starts IO event loop pub fn start() -> Result, UtilError> { let panic_handler = PanicHandler::new_in_arc(); - let mut event_loop = EventLoop::new().unwrap(); + let mut config = EventLoopConfig::new(); + config.messages_per_tick(1024); + let mut event_loop = EventLoop::configured(config).expect("Error creating event loop"); let channel = event_loop.channel(); let panic = panic_handler.clone(); let thread = thread::spawn(move || { @@ -390,7 +393,7 @@ impl IoService where Message: Send + Sync + Clone + 'static { impl Drop for IoService where Message: Send + Sync + Clone { fn drop(&mut self) { trace!(target: "shutdown", "[IoService] Closing..."); - self.host_channel.send(IoMessage::Shutdown).unwrap(); + self.host_channel.send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e)); self.thread.take().unwrap().join().ok(); trace!(target: "shutdown", "[IoService] Closed."); } diff --git a/util/src/journaldb/archivedb.rs b/util/src/journaldb/archivedb.rs index 3713fc79e..40172cccd 100644 --- a/util/src/journaldb/archivedb.rs +++ b/util/src/journaldb/archivedb.rs @@ -48,13 +48,8 @@ pub struct ArchiveDB { impl ArchiveDB { /// Create a new instance from file - pub fn new(path: &str, cache_size: Option) -> ArchiveDB { - let opts = DatabaseConfig { - // this must match account_db prefix - prefix_size: Some(DB_PREFIX_LEN), - max_open_files: 256, - cache_size: cache_size, - }; + pub fn new(path: &str, config: DatabaseConfig) -> ArchiveDB { + let opts = config.prefix(DB_PREFIX_LEN); let backing = Database::open(&opts, path).unwrap_or_else(|e| { panic!("Error opening state db: {}", e); }); @@ -80,7 +75,7 @@ impl ArchiveDB { fn new_temp() -> ArchiveDB { let mut dir = env::temp_dir(); dir.push(H32::random().hex()); - Self::new(dir.to_str().unwrap(), None) + Self::new(dir.to_str().unwrap(), DatabaseConfig::default()) } fn payload(&self, key: &H256) -> Option { @@ -222,6 +217,7 @@ mod tests { use super::*; use hashdb::*; use journaldb::traits::JournalDB; + use kvdb::DatabaseConfig; #[test] fn insert_same_in_fork() { @@ -363,7 +359,7 @@ mod tests { let bar = H256::random(); let foo = { - let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); + let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); jdb.emplace(bar.clone(), b"bar".to_vec()); @@ -372,13 +368,13 @@ mod tests { }; { - let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); + let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.remove(&foo); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); } { - let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); + let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); assert!(jdb.contains(&foo)); assert!(jdb.contains(&bar)); jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap(); @@ -391,7 +387,7 @@ mod tests { dir.push(H32::random().hex()); let foo = { - let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); + let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); jdb.commit(0, &b"0".sha3(), None).unwrap(); @@ -405,7 +401,7 @@ mod tests { }; { - let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); + let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.remove(&foo); jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap(); assert!(jdb.contains(&foo)); @@ -420,7 +416,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); let (foo, _, _) = { - let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); + let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); let bar = jdb.insert(b"bar"); @@ -435,7 +431,7 @@ mod tests { }; { - let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), None); + let mut jdb = ArchiveDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap(); assert!(jdb.contains(&foo)); } @@ -446,14 +442,14 @@ mod tests { let temp = ::devtools::RandomTempPath::new(); let key = { - let mut jdb = ArchiveDB::new(temp.as_str(), None); + let mut jdb = ArchiveDB::new(temp.as_str(), DatabaseConfig::default()); let key = jdb.insert(b"foo"); jdb.commit(0, &b"0".sha3(), None).unwrap(); key }; { - let jdb = ArchiveDB::new(temp.as_str(), None); + let jdb = ArchiveDB::new(temp.as_str(), DatabaseConfig::default()); let state = jdb.state(&key); assert!(state.is_some()); } diff --git a/util/src/journaldb/earlymergedb.rs b/util/src/journaldb/earlymergedb.rs index 71959fa35..2b87f8345 100644 --- a/util/src/journaldb/earlymergedb.rs +++ b/util/src/journaldb/earlymergedb.rs @@ -73,13 +73,8 @@ const PADDING : [u8; 10] = [ 0u8; 10 ]; impl EarlyMergeDB { /// Create a new instance from file - pub fn new(path: &str, cache_size: Option) -> EarlyMergeDB { - let opts = DatabaseConfig { - // this must match account_db prefix - prefix_size: Some(DB_PREFIX_LEN), - max_open_files: 256, - cache_size: cache_size, - }; + pub fn new(path: &str, config: DatabaseConfig) -> EarlyMergeDB { + let opts = config.prefix(DB_PREFIX_LEN); let backing = Database::open(&opts, path).unwrap_or_else(|e| { panic!("Error opening state db: {}", e); }); @@ -107,7 +102,7 @@ impl EarlyMergeDB { fn new_temp() -> EarlyMergeDB { let mut dir = env::temp_dir(); dir.push(H32::random().hex()); - Self::new(dir.to_str().unwrap(), None) + Self::new(dir.to_str().unwrap(), DatabaseConfig::default()) } fn morph_key(key: &H256, index: u8) -> Bytes { @@ -537,6 +532,7 @@ mod tests { use super::super::traits::JournalDB; use hashdb::*; use log::init_log; + use kvdb::DatabaseConfig; #[test] fn insert_same_in_fork() { @@ -714,7 +710,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(0, &b"0".sha3(), None).unwrap(); assert!(jdb.can_reconstruct_refs()); @@ -742,7 +738,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(0, &b"0".sha3(), None).unwrap(); assert!(jdb.can_reconstruct_refs()); @@ -770,7 +766,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(0, &b"0".sha3(), None).unwrap(); assert!(jdb.can_reconstruct_refs()); @@ -808,7 +804,7 @@ mod tests { let bar = H256::random(); let foo = { - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); jdb.emplace(bar.clone(), b"bar".to_vec()); @@ -818,14 +814,14 @@ mod tests { }; { - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.remove(&foo); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); assert!(jdb.can_reconstruct_refs()); } { - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); assert!(jdb.contains(&foo)); assert!(jdb.contains(&bar)); jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap(); @@ -840,7 +836,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 4 let foo = jdb.insert(b"foo"); @@ -869,7 +865,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 4 let foo = jdb.insert(b"foo"); @@ -918,7 +914,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); @@ -949,7 +945,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 4 let foo = jdb.insert(b"foo"); jdb.commit(0, &b"0".sha3(), None).unwrap(); @@ -989,7 +985,7 @@ mod tests { let foo = b"foo".sha3(); { - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 jdb.insert(b"foo"); jdb.commit(0, &b"0".sha3(), None).unwrap(); @@ -1010,7 +1006,7 @@ mod tests { assert!(jdb.contains(&foo)); // incantation to reopen the db - }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.remove(&foo); jdb.commit(4, &b"4".sha3(), Some((2, b"2".sha3()))).unwrap(); @@ -1018,14 +1014,14 @@ mod tests { assert!(jdb.contains(&foo)); // incantation to reopen the db - }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(5, &b"5".sha3(), Some((3, b"3".sha3()))).unwrap(); assert!(jdb.can_reconstruct_refs()); assert!(jdb.contains(&foo)); // incantation to reopen the db - }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + }; { let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(6, &b"6".sha3(), Some((4, b"4".sha3()))).unwrap(); assert!(jdb.can_reconstruct_refs()); @@ -1038,7 +1034,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); let (foo, bar, baz) = { - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); let bar = jdb.insert(b"bar"); @@ -1056,7 +1052,7 @@ mod tests { }; { - let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), None); + let mut jdb = EarlyMergeDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap(); assert!(jdb.can_reconstruct_refs()); assert!(jdb.contains(&foo)); diff --git a/util/src/journaldb/mod.rs b/util/src/journaldb/mod.rs index 973dd9eb0..4715273dd 100644 --- a/util/src/journaldb/mod.rs +++ b/util/src/journaldb/mod.rs @@ -17,6 +17,7 @@ //! `JournalDB` interface and implementation. use common::*; +use kvdb::DatabaseConfig; /// Export the journaldb module. pub mod traits; @@ -71,12 +72,12 @@ impl fmt::Display for Algorithm { } /// Create a new `JournalDB` trait object. -pub fn new(path: &str, algorithm: Algorithm, cache_size: Option) -> Box { +pub fn new(path: &str, algorithm: Algorithm, config: DatabaseConfig) -> Box { match algorithm { - Algorithm::Archive => Box::new(archivedb::ArchiveDB::new(path, cache_size)), - Algorithm::EarlyMerge => Box::new(earlymergedb::EarlyMergeDB::new(path, cache_size)), - Algorithm::OverlayRecent => Box::new(overlayrecentdb::OverlayRecentDB::new(path, cache_size)), - Algorithm::RefCounted => Box::new(refcounteddb::RefCountedDB::new(path, cache_size)), + Algorithm::Archive => Box::new(archivedb::ArchiveDB::new(path, config)), + Algorithm::EarlyMerge => Box::new(earlymergedb::EarlyMergeDB::new(path, config)), + Algorithm::OverlayRecent => Box::new(overlayrecentdb::OverlayRecentDB::new(path, config)), + Algorithm::RefCounted => Box::new(refcounteddb::RefCountedDB::new(path, config)), } } diff --git a/util/src/journaldb/overlayrecentdb.rs b/util/src/journaldb/overlayrecentdb.rs index 158b771fb..0ffea308b 100644 --- a/util/src/journaldb/overlayrecentdb.rs +++ b/util/src/journaldb/overlayrecentdb.rs @@ -98,18 +98,13 @@ const PADDING : [u8; 10] = [ 0u8; 10 ]; impl OverlayRecentDB { /// Create a new instance from file - pub fn new(path: &str, cache_size: Option) -> OverlayRecentDB { - Self::from_prefs(path, cache_size) + pub fn new(path: &str, config: DatabaseConfig) -> OverlayRecentDB { + Self::from_prefs(path, config) } /// Create a new instance from file - pub fn from_prefs(path: &str, cache_size: Option) -> OverlayRecentDB { - let opts = DatabaseConfig { - // this must match account_db prefix - prefix_size: Some(DB_PREFIX_LEN), - max_open_files: 256, - cache_size: cache_size, - }; + pub fn from_prefs(path: &str, config: DatabaseConfig) -> OverlayRecentDB { + let opts = config.prefix(DB_PREFIX_LEN); let backing = Database::open(&opts, path).unwrap_or_else(|e| { panic!("Error opening state db: {}", e); }); @@ -135,7 +130,7 @@ impl OverlayRecentDB { pub fn new_temp() -> OverlayRecentDB { let mut dir = env::temp_dir(); dir.push(H32::random().hex()); - Self::new(dir.to_str().unwrap(), None) + Self::new(dir.to_str().unwrap(), DatabaseConfig::default()) } #[cfg(test)] @@ -369,6 +364,7 @@ mod tests { use hashdb::*; use log::init_log; use journaldb::JournalDB; + use kvdb::DatabaseConfig; #[test] fn insert_same_in_fork() { @@ -526,7 +522,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(0, &b"0".sha3(), None).unwrap(); assert!(jdb.can_reconstruct_refs()); @@ -554,7 +550,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(0, &b"0".sha3(), None).unwrap(); assert!(jdb.can_reconstruct_refs()); @@ -582,7 +578,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(0, &b"0".sha3(), None).unwrap(); assert!(jdb.can_reconstruct_refs()); @@ -620,7 +616,7 @@ mod tests { let bar = H256::random(); let foo = { - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); jdb.emplace(bar.clone(), b"bar".to_vec()); @@ -630,14 +626,14 @@ mod tests { }; { - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.remove(&foo); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); assert!(jdb.can_reconstruct_refs()); } { - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); assert!(jdb.contains(&foo)); assert!(jdb.contains(&bar)); jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap(); @@ -652,7 +648,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 4 let foo = jdb.insert(b"foo"); @@ -681,7 +677,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 4 let foo = jdb.insert(b"foo"); @@ -730,7 +726,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap(); @@ -761,7 +757,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 4 let foo = jdb.insert(b"foo"); jdb.commit(0, &b"0".sha3(), None).unwrap(); @@ -801,7 +797,7 @@ mod tests { let foo = b"foo".sha3(); { - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 jdb.insert(b"foo"); jdb.commit(0, &b"0".sha3(), None).unwrap(); @@ -822,7 +818,7 @@ mod tests { assert!(jdb.contains(&foo)); // incantation to reopen the db - }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.remove(&foo); jdb.commit(4, &b"4".sha3(), Some((2, b"2".sha3()))).unwrap(); @@ -830,14 +826,14 @@ mod tests { assert!(jdb.contains(&foo)); // incantation to reopen the db - }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(5, &b"5".sha3(), Some((3, b"3".sha3()))).unwrap(); assert!(jdb.can_reconstruct_refs()); assert!(jdb.contains(&foo)); // incantation to reopen the db - }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + }; { let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(6, &b"6".sha3(), Some((4, b"4".sha3()))).unwrap(); assert!(jdb.can_reconstruct_refs()); @@ -850,7 +846,7 @@ mod tests { let mut dir = ::std::env::temp_dir(); dir.push(H32::random().hex()); let (foo, bar, baz) = { - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); // history is 1 let foo = jdb.insert(b"foo"); let bar = jdb.insert(b"bar"); @@ -868,7 +864,7 @@ mod tests { }; { - let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), None); + let mut jdb = OverlayRecentDB::new(dir.to_str().unwrap(), DatabaseConfig::default()); jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap(); assert!(jdb.can_reconstruct_refs()); assert!(jdb.contains(&foo)); diff --git a/util/src/journaldb/refcounteddb.rs b/util/src/journaldb/refcounteddb.rs index fc7da1541..4be6a1eda 100644 --- a/util/src/journaldb/refcounteddb.rs +++ b/util/src/journaldb/refcounteddb.rs @@ -46,13 +46,8 @@ const PADDING : [u8; 10] = [ 0u8; 10 ]; impl RefCountedDB { /// Create a new instance given a `backing` database. - pub fn new(path: &str, cache_size: Option) -> RefCountedDB { - let opts = DatabaseConfig { - // this must match account_db prefix - prefix_size: Some(DB_PREFIX_LEN), - max_open_files: 256, - cache_size: cache_size, - }; + pub fn new(path: &str, config: DatabaseConfig) -> RefCountedDB { + let opts = config.prefix(DB_PREFIX_LEN); let backing = Database::open(&opts, path).unwrap_or_else(|e| { panic!("Error opening state db: {}", e); }); @@ -82,7 +77,7 @@ impl RefCountedDB { fn new_temp() -> RefCountedDB { let mut dir = env::temp_dir(); dir.push(H32::random().hex()); - Self::new(dir.to_str().unwrap(), None) + Self::new(dir.to_str().unwrap(), DatabaseConfig::default()) } } diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index 67a7243de..8a19e48bf 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -20,8 +20,6 @@ use std::default::Default; use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBVector, DBIterator, IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction}; -const DB_FILE_SIZE_BASE: u64 = 16 * 1024 * 1024; -const DB_FILE_SIZE_MULTIPLIER: i32 = 5; const DB_BACKGROUND_FLUSHES: i32 = 2; const DB_BACKGROUND_COMPACTIONS: i32 = 2; @@ -53,6 +51,36 @@ impl DBTransaction { } } +/// Compaction profile for the database settings +pub struct CompactionProfile { + /// L0-L1 target file size + pub initial_file_size: u64, + /// L2-LN target file size multiplier + pub file_size_multiplier: i32, + /// rate limiter for background flushes and compactions, bytes/sec, if any + pub write_rate_limit: Option, +} + +impl CompactionProfile { + /// Default profile suitable for most storage + pub fn default() -> CompactionProfile { + CompactionProfile { + initial_file_size: 32 * 1024 * 1024, + file_size_multiplier: 2, + write_rate_limit: None, + } + } + + /// Slow hdd compaction profile + pub fn hdd() -> CompactionProfile { + CompactionProfile { + initial_file_size: 192 * 1024 * 1024, + file_size_multiplier: 1, + write_rate_limit: Some(8 * 1024 * 1024), + } + } +} + /// Database configuration pub struct DatabaseConfig { /// Optional prefix size in bytes. Allows lookup by partial key. @@ -61,6 +89,8 @@ pub struct DatabaseConfig { pub max_open_files: i32, /// Cache-size pub cache_size: Option, + /// Compaction profile + pub compaction: CompactionProfile, } impl DatabaseConfig { @@ -69,9 +99,22 @@ impl DatabaseConfig { DatabaseConfig { cache_size: Some(cache_size), prefix_size: None, - max_open_files: -1, + max_open_files: 256, + compaction: CompactionProfile::default(), } } + + /// Modify the compaction profile + pub fn compaction(mut self, profile: CompactionProfile) -> Self { + self.compaction = profile; + self + } + + /// Modify the prefix of the db + pub fn prefix(mut self, prefix_size: usize) -> Self { + self.prefix_size = Some(prefix_size); + self + } } impl Default for DatabaseConfig { @@ -79,7 +122,8 @@ impl Default for DatabaseConfig { DatabaseConfig { cache_size: None, prefix_size: None, - max_open_files: -1, + max_open_files: 256, + compaction: CompactionProfile::default(), } } } @@ -111,13 +155,18 @@ impl Database { /// Open database file. Creates if it does not exist. pub fn open(config: &DatabaseConfig, path: &str) -> Result { let mut opts = Options::new(); - try!(opts.set_parsed_options("rate_limiter_bytes_per_sec=256000000")); + if let Some(rate_limit) = config.compaction.write_rate_limit { + try!(opts.set_parsed_options(&format!("rate_limiter_bytes_per_sec={}", rate_limit))); + } opts.set_max_open_files(config.max_open_files); opts.create_if_missing(true); opts.set_use_fsync(false); + + // compaction settings opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction); - opts.set_target_file_size_base(DB_FILE_SIZE_BASE); - opts.set_target_file_size_multiplier(DB_FILE_SIZE_MULTIPLIER); + opts.set_target_file_size_base(config.compaction.initial_file_size); + opts.set_target_file_size_multiplier(config.compaction.file_size_multiplier); + opts.set_max_background_flushes(DB_BACKGROUND_FLUSHES); opts.set_max_background_compactions(DB_BACKGROUND_COMPACTIONS); if let Some(cache_size) = config.cache_size { @@ -150,7 +199,16 @@ impl Database { opts.set_block_based_table_factory(&block_opts); opts.set_prefix_extractor_fixed_size(size); } - let db = try!(DB::open(&opts, path)); + let db = match DB::open(&opts, path) { + Ok(db) => db, + Err(ref s) if s.starts_with("Corruption:") => { + info!("{}", s); + info!("Attempting DB repair for {}", path); + try!(DB::repair(&opts, path)); + try!(DB::open(&opts, path)) + }, + Err(s) => { return Err(s); } + }; Ok(Database { db: db }) } @@ -244,10 +302,10 @@ mod tests { let path = RandomTempPath::create_dir(); let smoke = Database::open_default(path.as_path().to_str().unwrap()).unwrap(); assert!(smoke.is_empty()); - test_db(&DatabaseConfig { prefix_size: None, max_open_files: 256, cache_size: None, }); - test_db(&DatabaseConfig { prefix_size: Some(1), max_open_files: 256, cache_size: None, }); - test_db(&DatabaseConfig { prefix_size: Some(8), max_open_files: 256, cache_size: None, }); - test_db(&DatabaseConfig { prefix_size: Some(32), max_open_files: 256, cache_size: None, }); + test_db(&DatabaseConfig::default()); + test_db(&DatabaseConfig::default().prefix(12)); + test_db(&DatabaseConfig::default().prefix(22)); + test_db(&DatabaseConfig::default().prefix(8)); } } diff --git a/util/src/lib.rs b/util/src/lib.rs index adaf08e77..31e072dc7 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -117,6 +117,7 @@ extern crate libc; extern crate target_info; extern crate bigint; extern crate chrono; +extern crate ansi_term; pub mod standard; #[macro_use] diff --git a/util/src/log.rs b/util/src/log.rs index 172957c13..1dddae1cb 100644 --- a/util/src/log.rs +++ b/util/src/log.rs @@ -20,7 +20,21 @@ use std::env; use rlog::{LogLevelFilter}; use env_logger::LogBuilder; use std::sync::{RwLock, RwLockReadGuard}; +use std::sync::atomic::{Ordering, AtomicBool}; use arrayvec::ArrayVec; +pub use ansi_term::{Colour, Style}; + +lazy_static! { + static ref USE_COLOR: AtomicBool = AtomicBool::new(false); +} + +/// Paint, using colour if desired. +pub fn paint(c: Style, t: String) -> String { + match USE_COLOR.load(Ordering::Relaxed) { + true => format!("{}", c.paint(t)), + false => t, + } +} lazy_static! { static ref LOG_DUMMY: bool = { @@ -57,7 +71,8 @@ impl RotatingLogger { /// Creates new `RotatingLogger` with given levels. /// It does not enforce levels - it's just read only. - pub fn new(levels: String) -> Self { + pub fn new(levels: String, enable_color: bool) -> Self { + USE_COLOR.store(enable_color, Ordering::Relaxed); RotatingLogger { levels: levels, logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()), @@ -86,7 +101,7 @@ mod test { use super::RotatingLogger; fn logger() -> RotatingLogger { - RotatingLogger::new("test".to_owned()) + RotatingLogger::new("test".to_owned(), false) } #[test] diff --git a/util/src/network/discovery.rs b/util/src/network/discovery.rs index b67110538..d4f895702 100644 --- a/util/src/network/discovery.rs +++ b/util/src/network/discovery.rs @@ -28,7 +28,7 @@ use crypto::*; use rlp::*; use network::node_table::*; use network::error::NetworkError; -use io::StreamToken; +use io::{StreamToken, IoContext}; use network::PROTOCOL_VERSION; @@ -283,7 +283,7 @@ impl Discovery { ret } - pub fn writable(&mut self) { + pub fn writable(&mut self, io: &IoContext) where Message: Send + Sync + Clone { while !self.send_queue.is_empty() { let data = self.send_queue.pop_front().unwrap(); match self.udp_socket.send_to(&data.payload, &data.address) { @@ -302,15 +302,17 @@ impl Discovery { } } } + io.update_registration(self.token).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e)); } fn send_to(&mut self, payload: Bytes, address: SocketAddr) { self.send_queue.push_back(Datagramm { payload: payload, address: address }); } - pub fn readable(&mut self) -> Option { + pub fn readable(&mut self, io: &IoContext) -> Option where Message: Send + Sync + Clone { let mut buf: [u8; MAX_DATAGRAM_SIZE] = unsafe { mem::uninitialized() }; - match self.udp_socket.recv_from(&mut buf) { + let writable = !self.send_queue.is_empty(); + let res = match self.udp_socket.recv_from(&mut buf) { Ok(Some((len, address))) => self.on_packet(&buf[0..len], address).unwrap_or_else(|e| { debug!("Error processing UDP packet: {:?}", e); None @@ -320,7 +322,12 @@ impl Discovery { debug!("Error reading UPD socket: {:?}", e); None } + }; + let new_writable = !self.send_queue.is_empty(); + if writable != new_writable { + io.update_registration(self.token).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e)); } + res } fn on_packet(&mut self, packet: &[u8], from: SocketAddr) -> Result, NetworkError> { diff --git a/util/src/network/host.rs b/util/src/network/host.rs index 03f37fa61..a48d1544c 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -32,6 +32,8 @@ use misc::version; use crypto::*; use sha3::Hashable; use rlp::*; +use log::Colour::White; +use log::paint; use network::session::{Session, SessionData}; use error::*; use io::*; @@ -236,8 +238,8 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone } /// Send an IO message - pub fn message(&self, msg: Message) { - self.io.message(NetworkIoMessage::User(msg)); + pub fn message(&self, msg: Message) -> Result<(), UtilError> { + self.io.message(NetworkIoMessage::User(msg)) } /// Get an IoChannel. @@ -248,12 +250,14 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone /// Disable current protocol capability for given peer. If no capabilities left peer gets disconnected. pub fn disable_peer(&self, peer: PeerId) { //TODO: remove capability, disconnect if no capabilities left - self.io.message(NetworkIoMessage::DisablePeer(peer)); + self.io.message(NetworkIoMessage::DisablePeer(peer)) + .unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e)); } /// Disconnect peer. Reconnect can be attempted later. pub fn disconnect_peer(&self, peer: PeerId) { - self.io.message(NetworkIoMessage::Disconnect(peer)); + self.io.message(NetworkIoMessage::Disconnect(peer)) + .unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e)); } /// Check if the session is still active. @@ -267,7 +271,7 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone token: token, delay: ms, protocol: self.protocol, - }); + }).unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e)); Ok(()) } @@ -341,6 +345,7 @@ pub struct Host where Message: Send + Sync + Clone { reserved_nodes: RwLock>, num_sessions: AtomicUsize, stopping: AtomicBool, + first_time: AtomicBool, } impl Host where Message: Send + Sync + Clone { @@ -396,6 +401,7 @@ impl Host where Message: Send + Sync + Clone { reserved_nodes: RwLock::new(HashSet::new()), num_sessions: AtomicUsize::new(0), stopping: AtomicBool::new(false), + first_time: AtomicBool::new(true), }; for n in boot_nodes { @@ -531,7 +537,11 @@ impl Host where Message: Send + Sync + Clone { }; self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone()); - info!("Public node URL: {}", self.external_url().unwrap()); + + if self.first_time.load(AtomicOrdering::Relaxed) { + info!("Public node URL: {}", paint(White.bold(), format!("{}", self.external_url().unwrap()))); + self.first_time.store(false, AtomicOrdering::Relaxed); + } // Initialize discovery. let discovery = { @@ -714,7 +724,6 @@ impl Host where Message: Send + Sync + Clone { debug!(target: "network", "Can't accept connection: {:?}", e); } } - io.update_registration(TCP_ACCEPT).expect("Error registering TCP listener"); } fn session_writable(&self, token: StreamToken, io: &IoContext>) { @@ -910,11 +919,10 @@ impl IoHandler> for Host where Messa match stream { FIRST_SESSION ... LAST_SESSION => self.session_readable(stream, io), DISCOVERY => { - let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().readable() }; + let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().readable(io) }; if let Some(node_changes) = node_changes { self.update_nodes(io, node_changes); } - io.update_registration(DISCOVERY).expect("Error updating discovery registration"); }, TCP_ACCEPT => self.accept(io), _ => panic!("Received unknown readable token"), @@ -928,8 +936,7 @@ impl IoHandler> for Host where Messa match stream { FIRST_SESSION ... LAST_SESSION => self.session_writable(stream, io), DISCOVERY => { - self.discovery.lock().unwrap().as_mut().unwrap().writable(); - io.update_registration(DISCOVERY).expect("Error updating discovery registration"); + self.discovery.lock().unwrap().as_mut().unwrap().writable(io); } _ => panic!("Received unknown writable token"), } @@ -946,14 +953,14 @@ impl IoHandler> for Host where Messa FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io), DISCOVERY_REFRESH => { self.discovery.lock().unwrap().as_mut().unwrap().refresh(); - io.update_registration(DISCOVERY).expect("Error updating discovery registration"); + io.update_registration(DISCOVERY).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e)); }, DISCOVERY_ROUND => { let node_changes = { self.discovery.lock().unwrap().as_mut().unwrap().round() }; if let Some(node_changes) = node_changes { self.update_nodes(io, node_changes); } - io.update_registration(DISCOVERY).expect("Error updating discovery registration"); + io.update_registration(DISCOVERY).unwrap_or_else(|e| debug!("Error updating discovery registration: {:?}", e)); }, NODE_TABLE => { trace!(target: "network", "Refreshing node table"); @@ -1004,7 +1011,7 @@ impl IoHandler> for Host where Messa handler_token }; self.timers.write().unwrap().insert(handler_token, ProtocolTimer { protocol: protocol, token: *token }); - io.register_timer(handler_token, *delay).expect("Error registering timer"); + io.register_timer(handler_token, *delay).unwrap_or_else(|e| debug!("Error registering timer {}: {:?}", token, e)); }, NetworkIoMessage::Disconnect(ref peer) => { let session = { self.sessions.read().unwrap().get(*peer).cloned() }; diff --git a/util/src/network/tests.rs b/util/src/network/tests.rs index 861edc144..cd3f48d9a 100644 --- a/util/src/network/tests.rs +++ b/util/src/network/tests.rs @@ -88,7 +88,7 @@ impl NetworkProtocolHandler for TestProtocol { /// Timer function called after a timeout created with `NetworkContext::timeout`. fn timeout(&self, io: &NetworkContext, timer: TimerToken) { - io.message(TestProtocolMessage { payload: 22 }); + io.message(TestProtocolMessage { payload: 22 }).unwrap(); assert_eq!(timer, 0); self.got_timeout.store(true, AtomicOrdering::Relaxed); } diff --git a/util/src/rlp/bytes.rs b/util/src/rlp/bytes.rs index 1145ba27e..d252af828 100644 --- a/util/src/rlp/bytes.rs +++ b/util/src/rlp/bytes.rs @@ -258,14 +258,7 @@ impl FromBytes for T where T: FixedHash { Ordering::Equal => () }; - unsafe { - use std::{mem, ptr}; - - let mut res: T = mem::uninitialized(); - ptr::copy(bytes.as_ptr(), res.as_slice_mut().as_mut_ptr(), T::len()); - - Ok(res) - } + Ok(T::from_slice(bytes)) } } diff --git a/util/src/using_queue.rs b/util/src/using_queue.rs index a5a2b0465..e5e1a5a58 100644 --- a/util/src/using_queue.rs +++ b/util/src/using_queue.rs @@ -27,6 +27,14 @@ pub struct UsingQueue where T: Clone { max_size: usize, } +/// Take an item or just clone it? +pub enum GetAction { + /// Remove the item, faster but you can't get it back. + Take, + /// Clone the item, slower but you can get it again. + Clone, +} + impl UsingQueue where T: Clone { /// Create a new struct with a maximum size of `max_size`. pub fn new(max_size: usize) -> UsingQueue { @@ -74,6 +82,20 @@ impl UsingQueue where T: Clone { self.in_use.iter().position(|r| predicate(r)).map(|i| self.in_use.remove(i)) } + /// Returns `Some` item which is the first that `f` returns `true` with a reference to it + /// as a parameter or `None` if no such item exists in the queue. + pub fn clone_used_if

(&mut self, predicate: P) -> Option where P: Fn(&T) -> bool { + self.in_use.iter().find(|r| predicate(r)).cloned() + } + + /// Fork-function for `take_used_if` and `clone_used_if`. + pub fn get_used_if

(&mut self, action: GetAction, predicate: P) -> Option where P: Fn(&T) -> bool { + match action { + GetAction::Take => self.take_used_if(predicate), + GetAction::Clone => self.clone_used_if(predicate), + } + } + /// Returns the most recently pushed block if `f` returns `true` with a reference to it as /// a parameter, otherwise `None`. /// Will not destroy a block if a reference to it has previously been returned by `use_last_ref`, @@ -94,18 +116,66 @@ impl UsingQueue where T: Clone { } #[test] -fn should_find_when_pushed() { +fn should_not_find_when_pushed() { let mut q = UsingQueue::new(2); q.push(1); assert!(q.take_used_if(|i| i == &1).is_none()); } +#[test] +fn should_not_find_when_pushed_with_clone() { + let mut q = UsingQueue::new(2); + q.push(1); + assert!(q.clone_used_if(|i| i == &1).is_none()); +} + #[test] fn should_find_when_pushed_and_used() { let mut q = UsingQueue::new(2); q.push(1); q.use_last_ref(); - assert!(q.take_used_if(|i| i == &1).is_some()); + assert!(q.take_used_if(|i| i == &1).unwrap() == 1); +} + +#[test] +fn should_have_same_semantics_for_get_take_clone() { + let mut q = UsingQueue::new(2); + q.push(1); + assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none()); + assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none()); + q.use_last_ref(); + assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1); + assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1); + assert!(q.get_used_if(GetAction::Take, |i| i == &1).unwrap() == 1); + assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none()); + assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none()); +} + +#[test] +fn should_find_when_pushed_and_used_with_clone() { + let mut q = UsingQueue::new(2); + q.push(1); + q.use_last_ref(); + assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); +} + +#[test] +fn should_not_find_again_when_pushed_and_taken() { + let mut q = UsingQueue::new(2); + q.push(1); + q.use_last_ref(); + assert!(q.take_used_if(|i| i == &1).unwrap() == 1); + assert!(q.clone_used_if(|i| i == &1).is_none()); +} + +#[test] +fn should_find_again_when_pushed_and_cloned() { + let mut q = UsingQueue::new(2); + q.push(1); + q.use_last_ref(); + assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); + assert!(q.clone_used_if(|i| i == &1).unwrap() == 1); + assert!(q.take_used_if(|i| i == &1).unwrap() == 1); } #[test]