Merge branch 'master' into accountdb_migration

This commit is contained in:
Robert Habermeier 2016-07-08 16:14:53 +02:00
commit aaf9490c91
80 changed files with 830 additions and 27206 deletions

4
Cargo.lock generated
View File

@ -245,6 +245,7 @@ dependencies = [
"ethcore-devtools 1.3.0", "ethcore-devtools 1.3.0",
"ethcore-ipc 1.3.0", "ethcore-ipc 1.3.0",
"ethcore-ipc-codegen 1.3.0", "ethcore-ipc-codegen 1.3.0",
"ethcore-ipc-nano 1.3.0",
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethstore 0.1.0", "ethstore 0.1.0",
@ -256,6 +257,7 @@ dependencies = [
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -281,6 +283,7 @@ dependencies = [
"serde_codegen 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)", "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)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -330,6 +333,7 @@ dependencies = [
"ethash 1.3.0", "ethash 1.3.0",
"ethcore 1.3.0", "ethcore 1.3.0",
"ethcore-devtools 1.3.0", "ethcore-devtools 1.3.0",
"ethcore-ipc 1.3.0",
"ethcore-util 1.3.0", "ethcore-util 1.3.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethsync 1.3.0", "ethsync 1.3.0",

View File

@ -13,6 +13,7 @@ log = "0.3"
jsonrpc-core = "2.0" jsonrpc-core = "2.0"
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" } jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
unicase = "1.3"
url = "1.0" url = "1.0"
rustc-serialize = "0.3" rustc-serialize = "0.3"
serde = "0.7.0" serde = "0.7.0"

View File

@ -15,20 +15,23 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use std::sync::Arc;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
use api::types::{App, ApiError};
use api::response::{as_json, as_json_error};
use hyper::{server, net, Decoder, Encoder, Next}; use hyper::{server, net, Decoder, Encoder, Next};
use api::types::{App, ApiError};
use api::response::{as_json, as_json_error, ping_response};
use handlers::extract_url;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
#[derive(Clone)] #[derive(Clone)]
pub struct RestApi { pub struct RestApi {
local_domain: String,
endpoints: Arc<Endpoints>, endpoints: Arc<Endpoints>,
} }
impl RestApi { impl RestApi {
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> { pub fn new(local_domain: String, endpoints: Arc<Endpoints>) -> Box<Endpoint> {
Box::new(RestApi { Box::new(RestApi {
endpoints: endpoints local_domain: local_domain,
endpoints: endpoints,
}) })
} }
@ -59,9 +62,28 @@ struct RestApiRouter {
impl server::Handler<net::HttpStream> for RestApiRouter { impl server::Handler<net::HttpStream> for RestApiRouter {
fn on_request(&mut self, _request: server::Request<net::HttpStream>) -> Next { fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
self.handler = as_json(&self.api.list_apps()); let url = extract_url(&request);
Next::write() if url.is_none() {
// Just return 404 if we can't parse URL
return Next::write();
}
let url = url.expect("Check for None is above; qed");
let endpoint = url.path.get(1).map(|v| v.as_str());
let handler = endpoint.and_then(|v| match v {
"apps" => Some(as_json(&self.api.list_apps())),
"ping" => Some(ping_response(&self.api.local_domain)),
_ => None,
});
// Overwrite default
if let Some(h) = handler {
self.handler = h;
}
self.handler.on_request(request)
} }
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next { fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {

View File

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

View File

@ -19,5 +19,3 @@ include!("types.rs.in");
#[cfg(not(feature = "serde_macros"))] #[cfg(not(feature = "serde_macros"))]
include!(concat!(env!("OUT_DIR"), "/types.rs")); include!(concat!(env!("OUT_DIR"), "/types.rs"));

View File

@ -48,4 +48,3 @@ pub struct ApiError {
pub detail: String, pub detail: String,
} }

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

@ -0,0 +1,148 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Echo Handler
use std::io::Read;
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::method::Method;
use hyper::net::HttpStream;
use unicase::UniCase;
use super::ContentHandler;
#[derive(Debug, PartialEq)]
/// Type of Cross-Origin request
enum Cors {
/// Not a Cross-Origin request - no headers needed
No,
/// Cross-Origin request with valid Origin
Allowed(String),
/// Cross-Origin request with invalid Origin
Forbidden,
}
pub struct EchoHandler {
safe_origins: Vec<String>,
content: String,
cors: Cors,
handler: Option<ContentHandler>,
}
impl EchoHandler {
pub fn cors(safe_origins: Vec<String>) -> Self {
EchoHandler {
safe_origins: safe_origins,
content: String::new(),
cors: Cors::Forbidden,
handler: None,
}
}
fn cors_header(&self, origin: Option<String>) -> Cors {
fn origin_is_allowed(origin: &str, safe_origins: &[String]) -> bool {
for safe in safe_origins {
if origin.starts_with(safe) {
return true;
}
}
false
}
match origin {
Some(ref origin) if origin_is_allowed(origin, &self.safe_origins) => {
Cors::Allowed(origin.clone())
},
None => Cors::No,
_ => Cors::Forbidden,
}
}
}
impl server::Handler<HttpStream> for EchoHandler {
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
let origin = request.headers().get_raw("origin")
.and_then(|list| list.get(0))
.and_then(|origin| String::from_utf8(origin.clone()).ok());
self.cors = self.cors_header(origin);
// Don't even read the payload if origin is forbidden!
if let Cors::Forbidden = self.cors {
self.handler = Some(ContentHandler::ok(String::new(), "text/plain".into()));
Next::write()
} else {
Next::read()
}
}
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
match decoder.read_to_string(&mut self.content) {
Ok(0) => {
self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into()));
Next::write()
},
Ok(_) => Next::read(),
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::read(),
_ => Next::end(),
}
}
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
if let Cors::Allowed(ref domain) = self.cors {
let mut headers = res.headers_mut();
headers.set(header::Allow(vec![Method::Options, Method::Post, Method::Get]));
headers.set(header::AccessControlAllowHeaders(vec![
UniCase("origin".to_owned()),
UniCase("content-type".to_owned()),
UniCase("accept".to_owned()),
]));
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
}
self.handler.as_mut().unwrap().on_response(res)
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
self.handler.as_mut().unwrap().on_response_writable(encoder)
}
}
#[test]
fn should_return_correct_cors_value() {
// given
let safe_origins = vec!["chrome-extension://".to_owned(), "http://localhost:8080".to_owned()];
let cut = EchoHandler {
safe_origins: safe_origins,
content: String::new(),
cors: Cors::No,
handler: None,
};
// when
let res1 = cut.cors_header(Some("http://ethcore.io".into()));
let res2 = cut.cors_header(Some("http://localhost:8080".into()));
let res3 = cut.cors_header(Some("chrome-extension://deadbeefcafe".into()));
let res4 = cut.cors_header(None);
// then
assert_eq!(res1, Cors::Forbidden);
assert_eq!(res2, Cors::Allowed("http://localhost:8080".into()));
assert_eq!(res3, Cors::Allowed("chrome-extension://deadbeefcafe".into()));
assert_eq!(res4, Cors::No);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -31,6 +31,8 @@ bloomchain = "0.1"
"ethcore-ipc" = { path = "../ipc/rpc" } "ethcore-ipc" = { path = "../ipc/rpc" }
rayon = "0.3.1" rayon = "0.3.1"
ethstore = { path = "../ethstore" } ethstore = { path = "../ethstore" }
semver = "0.2"
ethcore-ipc-nano = { path = "../ipc/nano" }
[dependencies.hyper] [dependencies.hyper]
git = "https://github.com/ethcore/hyper" git = "https://github.com/ethcore/hyper"

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -183,7 +183,6 @@ pub struct OpenBlock<'x> {
engine: &'x Engine, engine: &'x Engine,
vm_factory: &'x EvmFactory, vm_factory: &'x EvmFactory,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
} }
/// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields, /// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
@ -195,7 +194,6 @@ pub struct ClosedBlock {
block: ExecutedBlock, block: ExecutedBlock,
uncle_bytes: Bytes, uncle_bytes: Bytes,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
unclosed_state: State, unclosed_state: State,
} }
@ -227,7 +225,6 @@ impl<'x> OpenBlock<'x> {
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
author: Address, author: Address,
gas_range_target: (U256, U256), gas_range_target: (U256, U256),
extra_data: Bytes, extra_data: Bytes,
@ -238,7 +235,6 @@ impl<'x> OpenBlock<'x> {
engine: engine, engine: engine,
vm_factory: vm_factory, vm_factory: vm_factory,
last_hashes: last_hashes, last_hashes: last_hashes,
dao_rescue_block_gas_limit: dao_rescue_block_gas_limit,
}; };
r.block.base.header.parent_hash = parent.hash(); r.block.base.header.parent_hash = parent.hash();
@ -295,7 +291,6 @@ impl<'x> OpenBlock<'x> {
/// Get the environment info concerning this block. /// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo { pub fn env_info(&self) -> EnvInfo {
// TODO: memoise. // TODO: memoise.
const SOFT_FORK_BLOCK: u64 = 1_800_000;
EnvInfo { EnvInfo {
number: self.block.base.header.number, number: self.block.base.header.number,
author: self.block.base.header.author.clone(), author: self.block.base.header.author.clone(),
@ -304,7 +299,6 @@ impl<'x> OpenBlock<'x> {
last_hashes: self.last_hashes.clone(), // TODO: should be a reference. last_hashes: self.last_hashes.clone(), // TODO: should be a reference.
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used), gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.block.base.header.gas_limit.clone(), gas_limit: self.block.base.header.gas_limit.clone(),
dao_rescue_block_gas_limit: if self.block.base.header.number == SOFT_FORK_BLOCK { Some(self.block.base.header.gas_limit) } else { self.dao_rescue_block_gas_limit },
} }
} }
@ -351,7 +345,6 @@ impl<'x> OpenBlock<'x> {
block: s.block, block: s.block,
uncle_bytes: uncle_bytes, uncle_bytes: uncle_bytes,
last_hashes: s.last_hashes, last_hashes: s.last_hashes,
dao_rescue_block_gas_limit: s.dao_rescue_block_gas_limit,
unclosed_state: unclosed_state, unclosed_state: unclosed_state,
} }
} }
@ -411,7 +404,6 @@ impl ClosedBlock {
engine: engine, engine: engine,
vm_factory: vm_factory, vm_factory: vm_factory,
last_hashes: self.last_hashes, last_hashes: self.last_hashes,
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit,
} }
} }
} }
@ -481,7 +473,6 @@ pub fn enact(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory, vm_factory: &EvmFactory,
trie_factory: TrieFactory, trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
@ -492,7 +483,7 @@ pub fn enact(
} }
} }
let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone())); let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone()));
b.set_difficulty(*header.difficulty()); b.set_difficulty(*header.difficulty());
b.set_gas_limit(*header.gas_limit()); b.set_gas_limit(*header.gas_limit());
b.set_timestamp(header.timestamp()); b.set_timestamp(header.timestamp());
@ -510,13 +501,12 @@ pub fn enact_bytes(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory, vm_factory: &EvmFactory,
trie_factory: TrieFactory, trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
let block = BlockView::new(block_bytes); let block = BlockView::new(block_bytes);
let header = block.header(); let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory, trie_factory) enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
@ -528,12 +518,11 @@ pub fn enact_verified(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory, vm_factory: &EvmFactory,
trie_factory: TrieFactory, trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
let view = BlockView::new(&block.bytes); let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory, trie_factory) enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
@ -545,12 +534,11 @@ pub fn enact_and_seal(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: LastHashes, last_hashes: LastHashes,
dao_rescue_block_gas_limit: Option<U256>,
vm_factory: &EvmFactory, vm_factory: &EvmFactory,
trie_factory: TrieFactory, trie_factory: TrieFactory,
) -> Result<SealedBlock, Error> { ) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view(); let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory, trie_factory)).seal(engine, header.seal()))) Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)).seal(engine, header.seal())))
} }
#[cfg(test)] #[cfg(test)]
@ -570,7 +558,7 @@ mod tests {
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let last_hashes = vec![genesis_header.hash()]; let last_hashes = vec![genesis_header.hash()];
let vm_factory = Default::default(); let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock(); let b = b.close_and_lock();
let _ = b.seal(engine.deref(), vec![]); let _ = b.seal(engine.deref(), vec![]);
} }
@ -586,7 +574,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let vm_factory = Default::default(); let vm_factory = Default::default();
let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() let b = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
.close_and_lock().seal(engine.deref(), vec![]).unwrap(); .close_and_lock().seal(engine.deref(), vec![]).unwrap();
let orig_bytes = b.rlp_bytes(); let orig_bytes = b.rlp_bytes();
let orig_db = b.drain(); let orig_db = b.drain();
@ -594,7 +582,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default(), Default::default()).unwrap(); let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default(), Default::default()).unwrap();
assert_eq!(e.rlp_bytes(), orig_bytes); assert_eq!(e.rlp_bytes(), orig_bytes);
@ -614,7 +602,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let vm_factory = Default::default(); let vm_factory = Default::default();
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, Default::default(), false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let mut uncle1_header = Header::new(); let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec(); uncle1_header.extra_data = b"uncle1".to_vec();
let mut uncle2_header = Header::new(); let mut uncle2_header = Header::new();
@ -629,7 +617,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()); spec.ensure_db_good(db.as_hashdb_mut());
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default(), Default::default()).unwrap(); let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], &Default::default(), Default::default()).unwrap();
let bytes = e.rlp_bytes(); let bytes = e.rlp_bytes();
assert_eq!(bytes, orig_bytes); assert_eq!(bytes, orig_bytes);

View File

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

View File

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

View File

@ -14,16 +14,35 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Blockchain database client.
use std::path::PathBuf; use std::path::PathBuf;
use std::collections::{HashSet, HashMap};
use std::ops::Deref;
use std::mem;
use std::collections::VecDeque;
use std::sync::*;
use std::path::Path;
use std::fmt;
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::time::Instant; use std::time::Instant;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering};
use util::*; // util
use util::numbers::*;
use util::panics::*; use util::panics::*;
use util::network::*;
use util::io::*;
use util::rlp;
use util::sha3::*;
use util::{Bytes};
use util::rlp::{RlpStream, Rlp, UntrustedRlp};
use util::journaldb;
use util::journaldb::JournalDB;
use util::kvdb::*;
use util::{Applyable, Stream, View, PerfTimer, Itertools, Colour};
// other
use views::BlockView; use views::BlockView;
use error::{ImportError, ExecutionError, BlockError, ImportResult}; use error::{ImportError, ExecutionError, BlockError, ImportResult};
use header::{BlockNumber}; use header::BlockNumber;
use state::State; use state::State;
use spec::Spec; use spec::Spec;
use engine::Engine; use engine::Engine;
@ -35,24 +54,29 @@ use verification::{PreverifiedBlock, Verifier};
use block::*; use block::*;
use transaction::{LocalizedTransaction, SignedTransaction, Action}; use transaction::{LocalizedTransaction, SignedTransaction, Action};
use blockchain::extras::TransactionAddress; use blockchain::extras::TransactionAddress;
use filter::Filter; use types::filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo}; use block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockID, TransactionID, UncleID, TraceId, Mode, ClientConfig, DatabaseCompactionProfile, use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig,
BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, TransactionImportError, DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient,
BlockImportError, TransactionImportResult}; TraceFilter, CallAnalytics, BlockImportError, Mode};
use client::Error as ClientError; use client::Error as ClientError;
use env_info::EnvInfo; use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address}; use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt; use receipt::LocalizedReceipt;
pub use blockchain::CacheSize as BlockChainCacheSize;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace; use trace;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService};
use util::TrieFactory;
use ipc::IpcConfig;
use ipc::binary::{BinaryConvertError};
// re-export
pub use types::blockchain_info::BlockChainInfo; pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus; pub use types::block_status::BlockStatus;
use evm::Factory as EvmFactory; pub use blockchain::CacheSize as BlockChainCacheSize;
use miner::{Miner, MinerService, AccountDetails};
const MAX_TX_QUEUE_SIZE: usize = 4096; const MAX_TX_QUEUE_SIZE: usize = 4096;
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2; const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
@ -119,6 +143,7 @@ pub struct Client {
liveness: AtomicBool, liveness: AtomicBool,
io_channel: IoChannel<NetSyncMessage>, io_channel: IoChannel<NetSyncMessage>,
queue_transactions: AtomicUsize, queue_transactions: AtomicUsize,
previous_enode: Mutex<Option<String>>,
} }
const HISTORY: u64 = 1200; const HISTORY: u64 = 1200;
@ -204,6 +229,7 @@ impl Client {
miner: miner, miner: miner,
io_channel: message_channel, io_channel: message_channel,
queue_transactions: AtomicUsize::new(0), queue_transactions: AtomicUsize::new(0),
previous_enode: Mutex::new(None),
}; };
Ok(Arc::new(client)) Ok(Arc::new(client))
} }
@ -258,7 +284,7 @@ impl Client {
let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let last_hashes = self.build_last_hashes(header.parent_hash.clone());
let db = self.state_db.lock().unwrap().boxed_clone(); let db = self.state_db.lock().unwrap().boxed_clone();
let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.dao_rescue_block_gas_limit(header.parent_hash.clone()), &self.vm_factory, self.trie_factory.clone()); let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, &self.vm_factory, self.trie_factory.clone());
if let Err(e) = enact_result { if let Err(e) = enact_result {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(()); return Err(());
@ -409,12 +435,8 @@ impl Client {
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize { pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
let _timer = PerfTimer::new("import_queued_transactions"); let _timer = PerfTimer::new("import_queued_transactions");
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst); self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
let fetch_account = |a: &Address| AccountDetails { let txs = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
nonce: self.latest_nonce(a), let results = self.miner.import_external_transactions(self, txs);
balance: self.latest_balance(a),
};
let tx = transactions.iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
let results = self.miner.import_transactions(self, tx, fetch_account);
results.len() results.len()
} }
@ -454,7 +476,7 @@ impl Client {
HeaderView::new(&self.best_block_header()).state_root(), HeaderView::new(&self.best_block_header()).state_root(),
self.engine.account_start_nonce(), self.engine.account_start_nonce(),
self.trie_factory.clone()) self.trie_factory.clone())
.expect("State root of best block header always valid.") .expect("State root of best block header always valid.")
} }
/// Get info on the cache. /// Get info on the cache.
@ -474,12 +496,12 @@ impl Client {
pub fn tick(&self) { pub fn tick(&self) {
self.chain.collect_garbage(); self.chain.collect_garbage();
self.block_queue.collect_garbage(); self.block_queue.collect_garbage();
match self.mode { match self.mode {
Mode::Dark(timeout) => { Mode::Dark(timeout) => {
let mut ss = self.sleep_state.lock().unwrap(); let mut ss = self.sleep_state.lock().unwrap();
if let Some(t) = ss.last_activity { if let Some(t) = ss.last_activity {
if Instant::now() > t + timeout { if Instant::now() > t + timeout {
self.sleep(); self.sleep();
ss.last_activity = None; ss.last_activity = None;
} }
@ -489,14 +511,14 @@ impl Client {
let mut ss = self.sleep_state.lock().unwrap(); let mut ss = self.sleep_state.lock().unwrap();
let now = Instant::now(); let now = Instant::now();
if let Some(t) = ss.last_activity { if let Some(t) = ss.last_activity {
if now > t + timeout { if now > t + timeout {
self.sleep(); self.sleep();
ss.last_activity = None; ss.last_activity = None;
ss.last_autosleep = Some(now); ss.last_autosleep = Some(now);
} }
} }
if let Some(t) = ss.last_autosleep { if let Some(t) = ss.last_autosleep {
if now > t + wakeup_after { if now > t + wakeup_after {
self.wake_up(); self.wake_up();
ss.last_activity = Some(now); ss.last_activity = Some(now);
ss.last_autosleep = None; ss.last_autosleep = None;
@ -563,8 +585,22 @@ impl Client {
} }
} }
} }
/// Notify us that the network has been started.
pub fn network_started(&self, url: &String) {
let mut previous_enode = self.previous_enode.lock().unwrap();
if let Some(ref u) = *previous_enode {
if u == url {
return;
}
}
*previous_enode = Some(url.clone());
info!(target: "mode", "Public node URL: {}", url.apply(Colour::White.bold()));
}
} }
#[derive(Ipc)]
#[ipc(client_ident="RemoteClient")]
impl BlockChainClient for Client { impl BlockChainClient for Client {
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> { fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
let header = self.block_header(BlockID::Latest).unwrap(); let header = self.block_header(BlockID::Latest).unwrap();
@ -578,7 +614,6 @@ impl BlockChainClient for Client {
last_hashes: last_hashes, last_hashes: last_hashes,
gas_used: U256::zero(), gas_used: U256::zero(),
gas_limit: U256::max_value(), gas_limit: U256::max_value(),
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit(view.parent_hash()),
}; };
// that's just a copy of the state. // that's just a copy of the state.
let mut state = self.state(); let mut state = self.state();
@ -802,8 +837,8 @@ impl BlockChainClient for Client {
receipt.logs.into_iter() receipt.logs.into_iter()
.enumerate() .enumerate()
.filter(|tuple| filter.matches(&tuple.1)) .filter(|tuple| filter.matches(&tuple.1))
.map(|(i, log)| LocalizedLogEntry { .map(|(i, log)| LocalizedLogEntry {
entry: log, entry: log,
block_hash: hash.clone(), block_hash: hash.clone(),
block_number: number, block_number: number,
transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new), transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new),
@ -813,7 +848,6 @@ impl BlockChainClient for Client {
.collect::<Vec<LocalizedLogEntry>>() .collect::<Vec<LocalizedLogEntry>>()
}) })
.collect::<Vec<LocalizedLogEntry>>() .collect::<Vec<LocalizedLogEntry>>()
}) })
.collect() .collect()
} }
@ -862,18 +896,6 @@ impl BlockChainClient for Client {
self.build_last_hashes(self.chain.best_block_hash()) self.build_last_hashes(self.chain.best_block_hash())
} }
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, TransactionImportError>> {
let fetch_account = |a: &Address| AccountDetails {
nonce: self.latest_nonce(a),
balance: self.latest_balance(a),
};
self.miner.import_transactions(self, transactions, &fetch_account)
.into_iter()
.map(|res| res.map_err(|e| e.into()))
.collect()
}
fn queue_transactions(&self, transactions: Vec<Bytes>) { fn queue_transactions(&self, transactions: Vec<Bytes>) {
if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE { if self.queue_transactions.load(AtomicOrdering::Relaxed) > MAX_TX_QUEUE_SIZE {
debug!("Ignoring {} transactions: queue is full", transactions.len()); debug!("Ignoring {} transactions: queue is full", transactions.len());
@ -908,7 +930,6 @@ impl MiningBlockChainClient for Client {
self.state_db.lock().unwrap().boxed_clone(), self.state_db.lock().unwrap().boxed_clone(),
&self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"), &self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"),
self.build_last_hashes(h.clone()), self.build_last_hashes(h.clone()),
self.dao_rescue_block_gas_limit(h.clone()),
author, author,
gas_range_target, gas_range_target,
extra_data, extra_data,
@ -970,3 +991,5 @@ impl MayPanic for Client {
self.panic_handler.on_panic(closure); self.panic_handler.on_panic(closure);
} }
} }
impl IpcConfig for Client { }

View File

@ -16,7 +16,6 @@
//! Blockchain database client. //! Blockchain database client.
mod client;
mod config; mod config;
mod error; mod error;
mod test_client; mod test_client;
@ -27,7 +26,7 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueue
pub use self::error::Error; pub use self::error::Error;
pub use types::ids::*; pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::trace::Filter as TraceFilter; pub use types::trace_filter::Filter as TraceFilter;
pub use executive::{Executed, Executive, TransactOptions}; pub use executive::{Executed, Executive, TransactOptions};
pub use env_info::{LastHashes, EnvInfo}; pub use env_info::{LastHashes, EnvInfo};
@ -42,28 +41,26 @@ use header::{BlockNumber};
use transaction::{LocalizedTransaction, SignedTransaction}; use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use filter::Filter; use filter::Filter;
use views::{HeaderView, BlockView}; use views::{BlockView};
use error::{ImportResult, ExecutionError}; use error::{ImportResult, ExecutionError};
use receipt::LocalizedReceipt; use receipt::LocalizedReceipt;
use trace::LocalizedTrace; use trace::LocalizedTrace;
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
pub use types::call_analytics::CallAnalytics;
pub use block_import_error::BlockImportError; pub use block_import_error::BlockImportError;
pub use transaction_import::{TransactionImportResult, TransactionImportError}; pub use transaction_import::TransactionImportResult;
pub use transaction_import::TransactionImportError;
/// Options concerning what analytics we run on the call. pub mod client {
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)] //! Blockchain database client.
pub struct CallAnalytics {
/// Make a transaction trace. #![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
pub transaction_tracing: bool, include!(concat!(env!("OUT_DIR"), "/client.ipc.rs"));
/// Make a VM trace.
pub vm_tracing: bool,
/// Make a diff.
pub state_diffing: bool,
} }
/// Blockchain database client. Owns and manages a blockchain and a block queue. /// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send { pub trait BlockChainClient : Sync + Send {
/// Should be called by any external-facing interface when actively using the client. /// Should be called by any external-facing interface when actively using the client.
/// To minimise chatter, there's no need to call more than once every 30s. /// To minimise chatter, there's no need to call more than once every 30s.
fn keep_alive(&self) {} fn keep_alive(&self) {}
@ -192,9 +189,6 @@ pub trait BlockChainClient : Sync + Send {
/// Get last hashes starting from best block. /// Get last hashes starting from best block.
fn last_hashes(&self) -> LastHashes; fn last_hashes(&self) -> LastHashes;
/// import transactions from network/other 3rd party
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, TransactionImportError>>;
/// Queue transactions for importing. /// Queue transactions for importing.
fn queue_transactions(&self, transactions: Vec<Bytes>); fn queue_transactions(&self, transactions: Vec<Bytes>);
@ -226,28 +220,6 @@ pub trait BlockChainClient : Sync + Send {
Err(()) Err(())
} }
} }
/// Get `Some` gas limit of SOFT_FORK_BLOCK, or `None` if chain is not yet that long.
fn dao_rescue_block_gas_limit(&self, chain_hash: H256) -> Option<U256> {
const SOFT_FORK_BLOCK: u64 = 1800000;
// shortcut if the canon chain is already known.
if self.chain_info().best_block_number > SOFT_FORK_BLOCK + 1000 {
return self.block_header(BlockID::Number(SOFT_FORK_BLOCK)).map(|header| HeaderView::new(&header).gas_limit());
}
// otherwise check according to `chain_hash`.
if let Some(mut header) = self.block_header(BlockID::Hash(chain_hash)) {
if HeaderView::new(&header).number() < SOFT_FORK_BLOCK {
None
} else {
while HeaderView::new(&header).number() != SOFT_FORK_BLOCK {
header = self.block_header(BlockID::Hash(HeaderView::new(&header).parent_hash())).expect("chain is complete; parent of chain entry must be in chain; qed");
}
Some(HeaderView::new(&header).gas_limit())
}
} else {
None
}
}
} }
/// Extended client interface used for mining /// Extended client interface used for mining

View File

@ -22,7 +22,7 @@ use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute; use blockchain::TreeRoute;
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics,
TransactionImportError, BlockImportError}; BlockImportError};
use header::{Header as BlockHeader, BlockNumber}; use header::{Header as BlockHeader, BlockNumber};
use filter::Filter; use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
@ -36,11 +36,9 @@ use spec::Spec;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock}; use block::{OpenBlock, SealedBlock};
use executive::Executed; use executive::Executed;
use error::{ExecutionError}; use error::ExecutionError;
use trace::LocalizedTrace; use trace::LocalizedTrace;
use miner::{TransactionImportResult, AccountDetails};
/// Test client. /// Test client.
pub struct TestBlockChainClient { pub struct TestBlockChainClient {
/// Blocks. /// Blocks.
@ -275,6 +273,10 @@ impl BlockChainClient for TestBlockChainClient {
} }
} }
fn latest_nonce(&self, address: &Address) -> U256 {
self.nonce(address, BlockID::Latest).unwrap()
}
fn code(&self, address: &Address) -> Option<Bytes> { fn code(&self, address: &Address) -> Option<Bytes> {
self.code.read().unwrap().get(address).cloned() self.code.read().unwrap().get(address).cloned()
} }
@ -287,6 +289,10 @@ impl BlockChainClient for TestBlockChainClient {
} }
} }
fn latest_balance(&self, address: &Address) -> U256 {
self.balance(address, BlockID::Latest).unwrap()
}
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> { fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> {
if let BlockID::Latest = id { if let BlockID::Latest = id {
Some(self.storage.read().unwrap().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)) Some(self.storage.read().unwrap().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new))
@ -488,24 +494,10 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!(); unimplemented!();
} }
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, TransactionImportError>> {
let nonces = self.nonces.read().unwrap();
let balances = self.balances.read().unwrap();
let fetch_account = |a: &Address| AccountDetails {
nonce: nonces[a],
balance: balances[a],
};
self.miner.import_transactions(self, transactions, &fetch_account)
.into_iter()
.map(|res| res.map_err(|e| e.into()))
.collect()
}
fn queue_transactions(&self, transactions: Vec<Bytes>) { fn queue_transactions(&self, transactions: Vec<Bytes>) {
// import right here // import right here
let tx = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect(); let txs = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
self.import_transactions(tx); self.miner.import_external_transactions(self, txs);
} }
fn pending_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self) -> Vec<SignedTransaction> {

View File

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

View File

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

View File

@ -20,9 +20,9 @@ use util::*;
use header::BlockNumber; use header::BlockNumber;
use basic_types::LogBloom; use basic_types::LogBloom;
use client::Error as ClientError; use client::Error as ClientError;
pub use types::executed::ExecutionError;
use ipc::binary::{BinaryConvertError, BinaryConvertable}; use ipc::binary::{BinaryConvertError, BinaryConvertable};
use types::block_import_error::BlockImportError; use types::block_import_error::BlockImportError;
pub use types::executed::ExecutionError;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
/// Errors concerning transaction processing. /// Errors concerning transaction processing.
@ -327,7 +327,6 @@ binary_fixed_size!(BlockError);
binary_fixed_size!(ImportError); binary_fixed_size!(ImportError);
binary_fixed_size!(TransactionError); binary_fixed_size!(TransactionError);
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted. // TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
/*#![feature(concat_idents)] /*#![feature(concat_idents)]
macro_rules! assimilate { macro_rules! assimilate {

View File

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

View File

@ -33,11 +33,8 @@ use super::spec::*;
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) } pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec. /// Create a new Frontier mainnet chain spec.
pub fn new_frontier(dao_rescue: bool) -> Spec { pub fn new_frontier() -> Spec {
Spec::load(match dao_rescue { Spec::load(include_bytes!("../../res/ethereum/frontier.json"))
true => include_bytes!("../../res/ethereum/frontier_dao_rescue.json"),
false => include_bytes!("../../res/ethereum/frontier.json"),
})
} }
/// Create a new Frontier chain spec as though it never changes to Homestead. /// Create a new Frontier chain spec as though it never changes to Homestead.
@ -89,7 +86,7 @@ mod tests {
#[test] #[test]
fn frontier() { fn frontier() {
let frontier = new_frontier(true); let frontier = new_frontier();
assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap()); assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap());
let genesis = frontier.genesis_block(); let genesis = frontier.genesis_block();

View File

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

View File

@ -95,6 +95,8 @@ extern crate rayon;
extern crate hyper; extern crate hyper;
extern crate ethash; extern crate ethash;
pub extern crate ethstore; pub extern crate ethstore;
extern crate semver;
extern crate ethcore_ipc_nano as nanoipc;
#[cfg(test)] extern crate ethcore_devtools as devtools; #[cfg(test)] extern crate ethcore_devtools as devtools;
#[cfg(feature = "jit" )] extern crate evmjit; #[cfg(feature = "jit" )] extern crate evmjit;
@ -106,7 +108,6 @@ pub mod block_queue;
pub mod client; pub mod client;
pub mod error; pub mod error;
pub mod ethereum; pub mod ethereum;
pub mod filter;
pub mod header; pub mod header;
pub mod service; pub mod service;
pub mod trace; pub mod trace;

View File

@ -20,7 +20,6 @@ use std::time::{Instant, Duration};
use util::*; use util::*;
use util::using_queue::{UsingQueue, GetAction}; use util::using_queue::{UsingQueue, GetAction};
use util::Colour::White;
use account_provider::AccountProvider; use account_provider::AccountProvider;
use views::{BlockView, HeaderView}; use views::{BlockView, HeaderView};
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
@ -316,6 +315,19 @@ impl Miner {
!have_work !have_work
} }
fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, origin: TransactionOrigin, transaction_queue: &mut TransactionQueue) ->
Vec<Result<TransactionImportResult, Error>> {
let fetch_account = |a: &Address| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
};
transactions.into_iter()
.map(|tx| transaction_queue.add(tx, &fetch_account, origin))
.collect()
}
/// Are we allowed to do a non-mandatory reseal? /// Are we allowed to do a non-mandatory reseal?
fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock().unwrap() } fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock().unwrap() }
} }
@ -356,7 +368,6 @@ impl MinerService for Miner {
last_hashes: last_hashes, last_hashes: last_hashes,
gas_used: U256::zero(), gas_used: U256::zero(),
gas_limit: U256::max_value(), gas_limit: U256::max_value(),
dao_rescue_block_gas_limit: chain.dao_rescue_block_gas_limit(header.parent_hash().clone()),
}; };
// that's just a copy of the state. // that's just a copy of the state.
let mut state = block.state().clone(); let mut state = block.state().clone();
@ -479,27 +490,24 @@ impl MinerService for Miner {
self.gas_range_target.read().unwrap().1 self.gas_range_target.read().unwrap().1
} }
fn import_transactions<T>(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) -> fn import_external_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>) ->
Vec<Result<TransactionImportResult, Error>> Vec<Result<TransactionImportResult, Error>> {
where T: Fn(&Address) -> AccountDetails {
let results: Vec<Result<TransactionImportResult, Error>> = { let mut transaction_queue = self.transaction_queue.lock().unwrap();
let mut transaction_queue = self.transaction_queue.lock().unwrap(); let results = self.add_transactions_to_queue(chain, transactions, TransactionOrigin::External,
transactions.into_iter() &mut transaction_queue);
.map(|tx| transaction_queue.add(tx, &fetch_account, TransactionOrigin::External))
.collect() if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() {
};
if !results.is_empty() && self.options.reseal_on_external_tx && self.tx_reseal_allowed() {
self.update_sealing(chain); self.update_sealing(chain);
} }
results results
} }
fn import_own_transaction<T>( fn import_own_transaction(
&self, &self,
chain: &MiningBlockChainClient, chain: &MiningBlockChainClient,
transaction: SignedTransaction, transaction: SignedTransaction,
fetch_account: T ) -> Result<TransactionImportResult, Error> {
) -> Result<TransactionImportResult, Error> where T: Fn(&Address) -> AccountDetails {
let hash = transaction.hash(); let hash = transaction.hash();
trace!(target: "own_tx", "Importing transaction: {:?}", transaction); trace!(target: "own_tx", "Importing transaction: {:?}", transaction);
@ -507,7 +515,7 @@ impl MinerService for Miner {
let imported = { let imported = {
// Be sure to release the lock before we call enable_and_prepare_sealing // Be sure to release the lock before we call enable_and_prepare_sealing
let mut transaction_queue = self.transaction_queue.lock().unwrap(); let mut transaction_queue = self.transaction_queue.lock().unwrap();
let import = transaction_queue.add(transaction, &fetch_account, TransactionOrigin::Local); let import = self.add_transactions_to_queue(chain, vec![transaction], TransactionOrigin::Local, &mut transaction_queue).pop().unwrap();
match import { match import {
Ok(ref res) => { Ok(ref res) => {
@ -646,7 +654,7 @@ impl MinerService for Miner {
let n = sealed.header().number(); let n = sealed.header().number();
let h = sealed.header().hash(); let h = sealed.header().hash();
try!(chain.import_sealed_block(sealed)); try!(chain.import_sealed_block(sealed));
info!(target: "miner", "Mined block imported OK. #{}: {}", paint(White.bold(), format!("{}", n)), paint(White.bold(), h.hex())); info!(target: "miner", "Mined block imported OK. #{}: {}", format!("{}", n).apply(Colour::White.bold()), h.hex().apply(Colour::White.bold()));
Ok(()) Ok(())
}) })
} }
@ -658,7 +666,12 @@ impl MinerService for Miner {
// Client should send message after commit to db and inserting to chain. // Client should send message after commit to db and inserting to chain.
.expect("Expected in-chain blocks."); .expect("Expected in-chain blocks.");
let block = BlockView::new(&block); let block = BlockView::new(&block);
block.transactions() let txs = block.transactions();
// populate sender
for tx in &txs {
let _sender = tx.sender();
}
txs
} }
// 1. We ignore blocks that were `imported` (because it means that they are not in canon-chain, and transactions // 1. We ignore blocks that were `imported` (because it means that they are not in canon-chain, and transactions
@ -675,14 +688,10 @@ impl MinerService for Miner {
.par_iter() .par_iter()
.map(|h| fetch_transactions(chain, h)); .map(|h| fetch_transactions(chain, h));
out_of_chain.for_each(|txs| { out_of_chain.for_each(|txs| {
// populate sender let mut transaction_queue = self.transaction_queue.lock().unwrap();
for tx in &txs { let _ = self.add_transactions_to_queue(
let _sender = tx.sender(); chain, txs, TransactionOrigin::External, &mut transaction_queue
} );
let _ = self.import_transactions(chain, txs, |a| AccountDetails {
nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a),
});
}); });
} }

View File

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

View File

@ -198,6 +198,7 @@ struct VerifiedTransaction {
/// transaction origin /// transaction origin
origin: TransactionOrigin, origin: TransactionOrigin,
} }
impl VerifiedTransaction { impl VerifiedTransaction {
fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result<Self, Error> { fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result<Self, Error> {
try!(transaction.sender()); try!(transaction.sender());

View File

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

View File

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

View File

@ -180,7 +180,6 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
db, db,
&last_header, &last_header,
last_hashes.clone(), last_hashes.clone(),
None,
author.clone(), author.clone(),
(3141562.into(), 31415620.into()), (3141562.into(), 31415620.into()),
vec![] vec![]

View File

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

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

@ -0,0 +1,71 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Client RPC tests
use nanoipc;
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicBool};
use client::{Client, BlockChainClient, ClientConfig, RemoteClient, BlockID};
use tests::helpers::*;
use devtools::*;
use miner::Miner;
use crossbeam;
use common::IoChannel;
pub fn run_test_worker(scope: &crossbeam::Scope, stop: Arc<AtomicBool>, socket_path: &str) {
let socket_path = socket_path.to_owned();
scope.spawn(move || {
let temp = RandomTempPath::create_dir();
let client = Client::new(
ClientConfig::default(),
get_test_spec(),
temp.as_path(),
Arc::new(Miner::with_spec(get_test_spec())),
IoChannel::disconnected()).unwrap();
let mut worker = nanoipc::Worker::new(&client);
worker.add_reqrep(&socket_path).unwrap();
while !stop.load(Ordering::Relaxed) {
worker.poll();
}
});
}
#[test]
fn can_handshake() {
crossbeam::scope(|scope| {
let stop_guard = StopGuard::new();
let socket_path = "ipc:///tmp/parity-client-rpc-10.ipc";
run_test_worker(scope, stop_guard.share(), socket_path);
let remote_client = nanoipc::init_client::<RemoteClient<_>>(socket_path).unwrap();
assert!(remote_client.handshake().is_ok());
})
}
#[test]
fn can_query_block() {
crossbeam::scope(|scope| {
let stop_guard = StopGuard::new();
let socket_path = "ipc:///tmp/parity-client-rpc-20.ipc";
run_test_worker(scope, stop_guard.share(), socket_path);
let remote_client = nanoipc::init_client::<RemoteClient<_>>(socket_path).unwrap();
let non_existant_block = remote_client.block_header(BlockID::Number(999));
assert!(non_existant_block.is_none());
})
}

View File

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

View File

@ -0,0 +1,38 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Block queue info types
use std::mem;
use ipc::binary::BinaryConvertError;
use std::collections::VecDeque;
/// Block queue status
#[derive(Debug, Binary)]
pub struct BlockQueueInfo {
/// Number of queued blocks pending verification
pub unverified_queue_size: usize,
/// Number of verified queued blocks pending import
pub verified_queue_size: usize,
/// Number of blocks being verified
pub verifying_queue_size: usize,
/// Configured maximum number of blocks in the queue
pub max_queue_size: usize,
/// Configured maximum number of bytes to use
pub max_mem_use: usize,
/// Heap memory used in bytes
pub mem_used: usize,
}

View File

@ -0,0 +1,32 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Call analytics related types
use std::mem;
use ipc::binary::{BinaryConvertError};
use std::collections::VecDeque;
/// Options concerning what analytics we run on the call.
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug, Binary)]
pub struct CallAnalytics {
/// Make a transaction trace.
pub transaction_tracing: bool,
/// Make a VM trace.
pub vm_tracing: bool,
/// Make a diff.
pub state_diffing: bool,
}

View File

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

View File

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

View File

@ -47,6 +47,7 @@ pub enum TransactionID {
} }
/// Uniquely identifies Trace. /// Uniquely identifies Trace.
#[derive(Binary)]
pub struct TraceId { pub struct TraceId {
/// Transaction /// Transaction
pub transaction: TransactionID, pub transaction: TransactionID,

View File

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

View File

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

View File

@ -0,0 +1,35 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Trace filter related types
use std::mem;
use ipc::binary::{BinaryConvertError};
use std::collections::VecDeque;
use std::ops::Range;
use util::{Address};
use types::ids::BlockID;
/// Easy to use trace filter.
#[derive(Binary)]
pub struct Filter {
/// Range of filtering.
pub range: Range<BlockID>,
/// From address.
pub from_address: Vec<Address>,
/// To address.
pub to_address: Vec<Address>,
}

View File

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

View File

@ -727,7 +727,6 @@ pub fn get_ipc_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]>
fn client_ident_renamed(cx: &ExtCtxt, item: &ast::Item) -> Option<String> { fn client_ident_renamed(cx: &ExtCtxt, item: &ast::Item) -> Option<String> {
for meta_items in item.attrs().iter().filter_map(get_ipc_meta_items) { for meta_items in item.attrs().iter().filter_map(get_ipc_meta_items) {
for meta_item in meta_items { for meta_item in meta_items {
let span = meta_item.span;
match meta_item.node { match meta_item.node {
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"client_ident" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"client_ident" => {
if let Ok(s) = get_str_from_lit(cx, name, lit) { if let Ok(s) = get_str_from_lit(cx, name, lit) {

View File

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

View File

@ -315,6 +315,31 @@ impl BinaryConvertable for String {
} }
} }
impl<T> BinaryConvertable for Range<T> where T: BinaryConvertable {
fn size(&self) -> usize {
mem::size_of::<T>() * 2
}
fn from_empty_bytes() -> Result<Self, BinaryConvertError> {
Err(BinaryConvertError)
}
fn to_bytes(&self, buffer: &mut[u8], length_stack: &mut VecDeque<usize>) -> Result<(), BinaryConvertError> {
try!(self.start.to_bytes(&mut buffer[..mem::size_of::<T>()], length_stack));
try!(self.end.to_bytes(&mut buffer[mem::size_of::<T>() + 1..], length_stack));
Ok(())
}
fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque<usize>) -> Result<Self, BinaryConvertError> {
Ok(try!(T::from_bytes(&buffer[..mem::size_of::<T>()], length_stack))..try!(T::from_bytes(&buffer[mem::size_of::<T>()+1..], length_stack)))
}
fn len_params() -> usize {
assert_eq!(0, T::len_params());
0
}
}
impl<T> BinaryConvertable for ::std::cell::RefCell<T> where T: BinaryConvertable { impl<T> BinaryConvertable for ::std::cell::RefCell<T> where T: BinaryConvertable {
fn size(&self) -> usize { fn size(&self) -> usize {
self.borrow().size() self.borrow().size()
@ -539,8 +564,6 @@ binary_fixed_size!(U512);
binary_fixed_size!(H256); binary_fixed_size!(H256);
binary_fixed_size!(H2048); binary_fixed_size!(H2048);
binary_fixed_size!(Address); binary_fixed_size!(Address);
binary_fixed_size!(Range<usize>);
binary_fixed_size!(Range<u64>);
#[test] #[test]
fn vec_serialize() { fn vec_serialize() {

View File

@ -91,7 +91,7 @@ pub fn invoke<W>(method_num: u16, params: &Option<Vec<u8>>, w: &mut W) where W:
} }
/// IpcSocket, read/write generalization /// IpcSocket, read/write generalization
pub trait IpcSocket: Read + Write + Sync { pub trait IpcSocket: Read + Write + Sync + Send {
} }
/// Basically something that needs only socket to be spawned /// Basically something that needs only socket to be spawned

View File

@ -53,8 +53,7 @@ mod tests {
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit" : "0x", "frontierCompatibilityModeLimit" : "0x"
"daoRescueSoftFork": true
} }
} }
}"#; }"#;

View File

@ -42,9 +42,6 @@ pub struct EthashParams {
/// Homestead transition block number. /// Homestead transition block number.
#[serde(rename="frontierCompatibilityModeLimit")] #[serde(rename="frontierCompatibilityModeLimit")]
pub frontier_compatibility_mode_limit: Uint, pub frontier_compatibility_mode_limit: Uint,
/// DAO rescue soft-fork?
#[serde(rename="daoRescueSoftFork")]
pub dao_rescue_soft_fork: bool,
} }
/// Ethash engine deserialization. /// Ethash engine deserialization.
@ -69,8 +66,7 @@ mod tests {
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar": "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar": "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0x42", "frontierCompatibilityModeLimit": "0x42"
"daoRescueSoftFork": true
} }
}"#; }"#;

View File

@ -63,8 +63,7 @@ mod tests {
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit" : "0x", "frontierCompatibilityModeLimit" : "0x"
"daoRescueSoftFork": false
} }
} }
}, },

View File

@ -57,9 +57,8 @@ Operating Options:
--fork POLICY Specifies the client's fork policy. POLICY must be --fork POLICY Specifies the client's fork policy. POLICY must be
one of: one of:
dogmatic - sticks rigidly to the standard chain. dogmatic - sticks rigidly to the standard chain.
dao-soft - votes for the DAO-rescue soft-fork. none - goes with whatever fork is decided but
normal - goes with whatever fork is decided but votes for none. [default: none].
votes for none. [default: normal].
Account Options: Account Options:
--unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution.

View File

@ -49,8 +49,7 @@ pub struct Directories {
#[derive(Eq, PartialEq, Debug)] #[derive(Eq, PartialEq, Debug)]
pub enum Policy { pub enum Policy {
DaoSoft, None,
Normal,
Dogmatic, Dogmatic,
} }
@ -135,33 +134,24 @@ impl Configuration {
pub fn policy(&self) -> Policy { pub fn policy(&self) -> Policy {
match self.args.flag_fork.as_str() { match self.args.flag_fork.as_str() {
"dao-soft" => Policy::DaoSoft, "none" => Policy::None,
"normal" => Policy::Normal,
"dogmatic" => Policy::Dogmatic, "dogmatic" => Policy::Dogmatic,
x => die!("{}: Invalid value given for --policy option. Use --help for more info.", x) x => die!("{}: Invalid value given for --policy option. Use --help for more info.", x)
} }
} }
pub fn gas_floor_target(&self) -> U256 { pub fn gas_floor_target(&self) -> U256 {
if self.policy() == Policy::DaoSoft { let d = &self.args.flag_gas_floor_target;
3_141_592.into() U256::from_dec_str(d).unwrap_or_else(|_| {
} else { die!("{}: Invalid target gas floor given. Must be a decimal unsigned 256-bit number.", d)
let d = &self.args.flag_gas_floor_target; })
U256::from_dec_str(d).unwrap_or_else(|_| {
die!("{}: Invalid target gas floor given. Must be a decimal unsigned 256-bit number.", d)
})
}
} }
pub fn gas_ceil_target(&self) -> U256 { pub fn gas_ceil_target(&self) -> U256 {
if self.policy() == Policy::DaoSoft { let d = &self.args.flag_gas_cap;
3_141_592.into() U256::from_dec_str(d).unwrap_or_else(|_| {
} else { die!("{}: Invalid target gas ceiling given. Must be a decimal unsigned 256-bit number.", d)
let d = &self.args.flag_gas_cap; })
U256::from_dec_str(d).unwrap_or_else(|_| {
die!("{}: Invalid target gas ceiling given. Must be a decimal unsigned 256-bit number.", d)
})
}
} }
pub fn gas_price(&self) -> U256 { pub fn gas_price(&self) -> U256 {
@ -191,7 +181,7 @@ impl Configuration {
let wei_per_usd: f32 = 1.0e18 / usd_per_eth; let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
let gas_per_tx: f32 = 21000.0; let gas_per_tx: f32 = 21000.0;
let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
info!("Using a conversion rate of Ξ1 = {} ({} wei/gas)", paint(White.bold(), format!("US${}", usd_per_eth)), paint(Yellow.bold(), format!("{}", wei_per_gas))); info!("Using a conversion rate of Ξ1 = {} ({} wei/gas)", format!("US${}", usd_per_eth).apply(White.bold()), format!("{}", wei_per_gas).apply(Yellow.bold()));
U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap() U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap()
} }
} }
@ -207,7 +197,7 @@ impl Configuration {
pub fn spec(&self) -> Spec { pub fn spec(&self) -> Spec {
match self.chain().as_str() { match self.chain().as_str() {
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(self.policy() != Policy::Dogmatic), "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
"morden" | "testnet" => ethereum::new_morden(), "morden" | "testnet" => ethereum::new_morden(),
"olympic" => ethereum::new_olympic(), "olympic" => ethereum::new_olympic(),
f => Spec::load(contents(f).unwrap_or_else(|_| { f => Spec::load(contents(f).unwrap_or_else(|_| {
@ -352,7 +342,7 @@ impl Configuration {
if let journaldb::Algorithm::Archive = client_config.pruning { if let journaldb::Algorithm::Archive = client_config.pruning {
client_config.trie_spec = TrieSpec::Fat; client_config.trie_spec = TrieSpec::Fat;
} else { } else {
die!("Fatdb is not supported. Please rerun with --pruning=archive") die!("Fatdb is not supported. Please re-run with --pruning=archive")
} }
} }
@ -367,7 +357,7 @@ impl Configuration {
}; };
if self.args.flag_jitvm { if self.args.flag_jitvm {
client_config.vm_type = VMType::jit().unwrap_or_else(|| die!("Parity built without jit vm.")) client_config.vm_type = VMType::jit().unwrap_or_else(|| die!("Parity is built without the JIT EVM."))
} }
trace!(target: "parity", "Using pruning strategy of {}", client_config.pruning); trace!(target: "parity", "Using pruning strategy of {}", client_config.pruning);
@ -439,11 +429,11 @@ impl Configuration {
fn geth_ipc_path(&self) -> String { fn geth_ipc_path(&self) -> String {
if cfg!(windows) { if cfg!(windows) {
r"\\.\pipe\geth.ipc".to_owned() r"\\.\pipe\geth.ipc".to_owned()
} } else {
else { match self.args.flag_testnet {
if self.args.flag_testnet { path::ethereum::with_testnet("geth.ipc") } true => path::ethereum::with_testnet("geth.ipc"),
else { path::ethereum::with_default("geth.ipc") } false => path::ethereum::with_default("geth.ipc"),
.to_str().unwrap().to_owned() }.to_str().unwrap().to_owned()
} }
} }

View File

@ -22,7 +22,7 @@ use std::time::{Instant, Duration};
use std::sync::RwLock; use std::sync::RwLock;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use ethsync::{EthSync, SyncProvider}; use ethsync::{EthSync, SyncProvider};
use util::Uint; use util::{Uint, NetworkService};
use ethcore::client::*; use ethcore::client::*;
use number_prefix::{binary_prefix, Standalone, Prefixed}; use number_prefix::{binary_prefix, Standalone, Prefixed};
@ -76,7 +76,7 @@ impl Informant {
} }
#[cfg_attr(feature="dev", allow(match_bool))] #[cfg_attr(feature="dev", allow(match_bool))]
pub fn tick(&self, client: &Client, maybe_sync: Option<&EthSync>) { pub fn tick<Message>(&self, client: &Client, maybe_sync: Option<(&EthSync, &NetworkService<Message>)>) where Message: Send + Sync + Clone + 'static {
let elapsed = self.last_tick.read().unwrap().elapsed(); let elapsed = self.last_tick.read().unwrap().elapsed();
if elapsed < Duration::from_secs(5) { if elapsed < Duration::from_secs(5) {
return; return;
@ -110,11 +110,13 @@ impl Informant {
paint(Yellow.bold(), format!("{:3}", ((report.gas_processed - last_report.gas_processed) / From::from(elapsed.as_milliseconds() * 1000)).low_u64())), paint(Yellow.bold(), format!("{:3}", ((report.gas_processed - last_report.gas_processed) / From::from(elapsed.as_milliseconds() * 1000)).low_u64())),
match maybe_sync { match maybe_sync {
Some(sync) => { Some((sync, net)) => {
let sync_info = sync.status(); let sync_info = sync.status();
format!("{}/{} peers {} ", let net_config = net.config();
format!("{}/{}/{} peers {} ",
paint(Green.bold(), format!("{:2}", sync_info.num_active_peers)), paint(Green.bold(), format!("{:2}", sync_info.num_active_peers)),
paint(Green.bold(), format!("{:2}", sync_info.num_peers)), paint(Green.bold(), format!("{:2}", sync_info.num_peers)),
paint(Green.bold(), format!("{:2}", net_config.ideal_peers)),
paint(Cyan.bold(), format!("{:>8}", format!("#{}", sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number)))), paint(Cyan.bold(), format!("{:>8}", format!("#{}", sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number)))),
) )
} }
@ -128,7 +130,7 @@ impl Informant {
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(cache_info.total()))), paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(cache_info.total()))),
paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(queue_info.mem_used))), paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(queue_info.mem_used))),
match maybe_sync { match maybe_sync {
Some(sync) => { Some((sync, _)) => {
let sync_info = sync.status(); let sync_info = sync.status();
format!(" {} sync", paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(sync_info.mem_used)))) format!(" {} sync", paint(Purple.bold(), format!("{:>8}", Informant::format_bytes(sync_info.mem_used))))
} }

View File

@ -40,7 +40,9 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) { fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
if let INFO_TIMER = timer { if let INFO_TIMER = timer {
self.info.tick(&self.client, Some(&self.sync)); if let Some(net) = self.network.upgrade() {
self.info.tick(&self.client, Some((&self.sync, &net)));
}
} }
} }

View File

@ -80,7 +80,7 @@ use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use rustc_serialize::hex::FromHex; use rustc_serialize::hex::FromHex;
use ctrlc::CtrlC; use ctrlc::CtrlC;
use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, paint, Colour, version}; use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, Colour, Applyable, version, journaldb};
use util::panics::{MayPanic, ForwardPanic, PanicHandler}; use util::panics::{MayPanic, ForwardPanic, PanicHandler};
use ethcore::client::{Mode, BlockID, BlockChainClient, ClientConfig, get_db_path, BlockImportError}; use ethcore::client::{Mode, BlockID, BlockChainClient, ClientConfig, get_db_path, BlockImportError};
use ethcore::error::{ImportError}; use ethcore::error::{ImportError};
@ -97,7 +97,7 @@ use rpc::RpcServer;
use signer::{SignerServer, new_token}; use signer::{SignerServer, new_token};
use dapps::WebappServer; use dapps::WebappServer;
use io_handler::ClientIoHandler; use io_handler::ClientIoHandler;
use configuration::Configuration; use configuration::{Policy, Configuration};
fn main() { fn main() {
let conf = Configuration::parse(); let conf = Configuration::parse();
@ -188,10 +188,21 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
// Raise fdlimit // Raise fdlimit
unsafe { ::fdlimit::raise_fd_limit(); } unsafe { ::fdlimit::raise_fd_limit(); }
info!("Starting {}", paint(Colour::White.bold(), format!("{}", version()))); info!("Starting {}", format!("{}", version()).apply(Colour::White.bold()));
info!("Using state DB journalling strategy {}", match client_config.pruning {
journaldb::Algorithm::Archive => "archive",
journaldb::Algorithm::EarlyMerge => "light",
journaldb::Algorithm::OverlayRecent => "fast",
journaldb::Algorithm::RefCounted => "basic",
}.apply(Colour::White.bold()));
let net_settings = conf.net_settings(&spec); // Display warning about using experimental journaldb types
let sync_config = conf.sync_config(&spec); match client_config.pruning {
journaldb::Algorithm::EarlyMerge | journaldb::Algorithm::RefCounted => {
warn!("Your chosen strategy is {}! You can re-run with --pruning to change.", "unstable".apply(Colour::Red.bold()));
}
_ => {}
}
// Display warning about using unlock with signer // Display warning about using unlock with signer
if conf.signer_enabled() && conf.args.flag_unlock.is_some() { if conf.signer_enabled() && conf.args.flag_unlock.is_some() {
@ -199,6 +210,14 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
warn!("NOTE that Signer will not ask you to confirm transactions from unlocked account."); warn!("NOTE that Signer will not ask you to confirm transactions from unlocked account.");
} }
// Check fork settings.
if conf.policy() != Policy::None {
warn!("Value given for --policy, yet no proposed forks exist. Ignoring.");
}
let net_settings = conf.net_settings(&spec);
let sync_config = conf.sync_config(&spec);
// Secret Store // Secret Store
let account_service = Arc::new(conf.account_service()); let account_service = Arc::new(conf.account_service());
@ -466,7 +485,7 @@ fn execute_import(conf: Configuration) {
Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); } Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); }
Err(e) => die!("Cannot import block: {:?}", e) Err(e) => die!("Cannot import block: {:?}", e)
} }
informant.tick(client.deref(), None); informant.tick::<&'static ()>(client.deref(), None);
}; };
match format { match format {

View File

@ -14,11 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate ansi_term;
use self::ansi_term::Colour::White;
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use util::{Colour, Applyable};
use util::panics::{PanicHandler, ForwardPanic}; use util::panics::{PanicHandler, ForwardPanic};
use util::path::restrict_permissions_owner; use util::path::restrict_permissions_owner;
use die::*; use die::*;
@ -67,7 +66,7 @@ pub fn new_token(path: String) -> io::Result<()> {
let mut codes = try!(signer::AuthCodes::from_file(&path)); let mut codes = try!(signer::AuthCodes::from_file(&path));
let code = try!(codes.generate_new()); let code = try!(codes.generate_new());
try!(codes.to_file(&path)); try!(codes.to_file(&path));
println!("This key code will authorise your System Signer UI: {}", White.bold().paint(code)); println!("This key code will authorise your System Signer UI: {}", code.apply(Colour::White.bold()));
Ok(()) Ok(())
} }

View File

@ -25,6 +25,7 @@ transient-hashmap = "0.1"
serde_macros = { version = "0.7.0", optional = true } serde_macros = { version = "0.7.0", optional = true }
clippy = { version = "0.0.78", optional = true} clippy = { version = "0.0.78", optional = true}
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
ethcore-ipc = { path = "../ipc/rpc" }
[build-dependencies] [build-dependencies]
serde_codegen = { version = "0.7.0", optional = true } serde_codegen = { version = "0.7.0", optional = true }

View File

@ -32,6 +32,7 @@ extern crate ethcore;
extern crate ethsync; extern crate ethsync;
extern crate transient_hashmap; extern crate transient_hashmap;
extern crate json_ipc_server as ipc; extern crate json_ipc_server as ipc;
extern crate ethcore_ipc;
#[cfg(test)] #[cfg(test)]
extern crate ethjson; extern crate ethjson;

View File

@ -30,6 +30,7 @@ use util::sha3::*;
use util::rlp::{encode, decode, UntrustedRlp, View}; use util::rlp::{encode, decode, UntrustedRlp, View};
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID}; use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID};
use ethcore::header::Header as BlockHeader;
use ethcore::block::IsBlock; use ethcore::block::IsBlock;
use ethcore::views::*; use ethcore::views::*;
use ethcore::ethereum::Ethash; use ethcore::ethereum::Ethash;
@ -42,7 +43,6 @@ use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncIn
use v1::helpers::CallRequest as CRequest; use v1::helpers::CallRequest as CRequest;
use v1::impls::{default_gas_price, dispatch_transaction, error_codes}; use v1::impls::{default_gas_price, dispatch_transaction, error_codes};
use serde; use serde;
use ethcore::header::Header as BlockHeader;
/// Eth rpc implementation. /// Eth rpc implementation.
pub struct EthClient<C, S, M, EM> where pub struct EthClient<C, S, M, EM> where

View File

@ -56,7 +56,7 @@ pub use self::rpc::RpcClient;
use v1::helpers::TransactionRequest; use v1::helpers::TransactionRequest;
use v1::types::H256 as NH256; use v1::types::H256 as NH256;
use ethcore::error::Error as EthcoreError; use ethcore::error::Error as EthcoreError;
use ethcore::miner::{AccountDetails, MinerService}; use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient; use ethcore::client::MiningBlockChainClient;
use ethcore::transaction::{Action, SignedTransaction, Transaction}; use ethcore::transaction::{Action, SignedTransaction, Transaction};
use ethcore::account_provider::{AccountProvider, Error as AccountError}; use ethcore::account_provider::{AccountProvider, Error as AccountError};
@ -80,12 +80,7 @@ fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedT
where C: MiningBlockChainClient, M: MinerService { where C: MiningBlockChainClient, M: MinerService {
let hash = NH256::from(signed_transaction.hash()); let hash = NH256::from(signed_transaction.hash());
let import = miner.import_own_transaction(client, signed_transaction, |a: &Address| { let import = miner.import_own_transaction(client, signed_transaction);
AccountDetails {
nonce: client.latest_nonce(&a),
balance: client.latest_balance(&a),
}
});
import import
.map_err(transaction_error) .map_err(transaction_error)

View File

@ -200,8 +200,7 @@ const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{
"durationLimit": "0x0d", "durationLimit": "0x0d",
"blockReward": "0x4563918244F40000", "blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0xffffffffffffffff", "frontierCompatibilityModeLimit": "0xffffffffffffffff"
"daoRescueSoftFork": false
} }
} }
}, },

View File

@ -23,7 +23,7 @@ use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics};
use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethcore::receipt::Receipt; use ethcore::receipt::Receipt;
use ethcore::miner::{MinerService, MinerStatus, AccountDetails, TransactionImportResult}; use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult};
/// Test miner service. /// Test miner service.
pub struct TestMinerService { pub struct TestMinerService {
@ -130,14 +130,13 @@ impl MinerService for TestMinerService {
} }
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_transactions<T>(&self, _chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>, fetch_account: T) -> fn import_external_transactions(&self, _chain: &MiningBlockChainClient, transactions: Vec<SignedTransaction>) ->
Vec<Result<TransactionImportResult, Error>> Vec<Result<TransactionImportResult, Error>> {
where T: Fn(&Address) -> AccountDetails {
// lets assume that all txs are valid // lets assume that all txs are valid
self.imported_transactions.lock().unwrap().extend_from_slice(&transactions); self.imported_transactions.lock().unwrap().extend_from_slice(&transactions);
for sender in transactions.iter().filter_map(|t| t.sender().ok()) { for sender in transactions.iter().filter_map(|t| t.sender().ok()) {
let nonce = self.last_nonce(&sender).unwrap_or(fetch_account(&sender).nonce); let nonce = self.last_nonce(&sender).expect("last_nonce must be populated in tests");
self.last_nonces.write().unwrap().insert(sender, nonce + U256::from(1)); self.last_nonces.write().unwrap().insert(sender, nonce + U256::from(1));
} }
transactions transactions
@ -147,9 +146,8 @@ impl MinerService for TestMinerService {
} }
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, _fetch_account: T) -> fn import_own_transaction(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction) ->
Result<TransactionImportResult, Error> Result<TransactionImportResult, Error> {
where T: Fn(&Address) -> AccountDetails {
// keep the pending nonces up to date // keep the pending nonces up to date
if let Ok(ref sender) = transaction.sender() { if let Ok(ref sender) = transaction.sender() {

View File

@ -84,7 +84,7 @@ impl Into<EthFilter> for Filter {
VariadicValue::Single(t) => Some(vec![t.into()]), VariadicValue::Single(t) => Some(vec![t.into()]),
VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect()) VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect())
}).filter_map(|m| m).collect()).into_iter(); }).filter_map(|m| m).collect()).into_iter();
[iter.next(), iter.next(), iter.next(), iter.next()] vec![iter.next(), iter.next(), iter.next(), iter.next()]
} }
} }
} }

View File

@ -162,7 +162,7 @@ pub enum Diff<T> where T: Serialize {
Changed(ChangedType<T>), Changed(ChangedType<T>),
} }
impl<T, U> From<account_diff::Diff<T>> for Diff<U> where T: Eq, U: Serialize + From<T> { impl<T, U> From<account_diff::Diff<T>> for Diff<U> where T: Eq + ::ethcore_ipc::BinaryConvertable, U: Serialize + From<T> {
fn from(c: account_diff::Diff<T>) -> Self { fn from(c: account_diff::Diff<T>) -> Self {
match c { match c {
account_diff::Diff::Same => Diff::Same, account_diff::Diff::Same => Diff::Same,
@ -205,7 +205,7 @@ impl Serialize for StateDiff {
impl From<state_diff::StateDiff> for StateDiff { impl From<state_diff::StateDiff> for StateDiff {
fn from(c: state_diff::StateDiff) -> Self { fn from(c: state_diff::StateDiff) -> Self {
StateDiff(c.0.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) StateDiff(c.raw.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
} }
} }

View File

@ -44,10 +44,10 @@
//! let mut service = NetworkService::new(NetworkConfiguration::new()).unwrap(); //! let mut service = NetworkService::new(NetworkConfiguration::new()).unwrap();
//! service.start().unwrap(); //! service.start().unwrap();
//! let dir = env::temp_dir(); //! let dir = env::temp_dir();
//! let miner = Miner::new(Default::default(), ethereum::new_frontier(true), None); //! let miner = Miner::new(Default::default(), ethereum::new_frontier(), None);
//! let client = Client::new( //! let client = Client::new(
//! ClientConfig::default(), //! ClientConfig::default(),
//! ethereum::new_frontier(true), //! ethereum::new_frontier(),
//! &dir, //! &dir,
//! miner, //! miner,
//! service.io().channel() //! service.io().channel()

View File

@ -248,6 +248,13 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
IoMessage::RemoveHandler { handler_id } => { IoMessage::RemoveHandler { handler_id } => {
// TODO: flush event loop // TODO: flush event loop
self.handlers.remove(handler_id); self.handlers.remove(handler_id);
// unregister timers
let mut timers = self.timers.write().unwrap();
let to_remove: Vec<_> = timers.keys().cloned().filter(|timer_id| timer_id / TOKENS_PER_HANDLER == handler_id).collect();
for timer_id in to_remove {
let timer = timers.remove(&timer_id).expect("to_remove only contains keys from timers; qed");
event_loop.clear_timeout(timer.timeout);
}
}, },
IoMessage::AddTimer { handler_id, token, delay } => { IoMessage::AddTimer { handler_id, token, delay } => {
let timer_id = token + handler_id * TOKENS_PER_HANDLER; let timer_id = token + handler_id * TOKENS_PER_HANDLER;

View File

@ -17,6 +17,7 @@
//! Common log helper functions //! Common log helper functions
use std::env; use std::env;
use std::borrow::Cow;
use rlog::{LogLevelFilter}; use rlog::{LogLevelFilter};
use env_logger::LogBuilder; use env_logger::LogBuilder;
use std::sync::{RwLock, RwLockReadGuard}; use std::sync::{RwLock, RwLockReadGuard};
@ -28,12 +29,20 @@ lazy_static! {
static ref USE_COLOR: AtomicBool = AtomicBool::new(false); static ref USE_COLOR: AtomicBool = AtomicBool::new(false);
} }
/// Paint, using colour if desired. /// Something which can be apply()ed.
pub fn paint(c: Style, t: String) -> String { pub trait Applyable: AsRef<str> {
match USE_COLOR.load(Ordering::Relaxed) { /// Apply the style `c` to ourself, returning us styled in that manner.
true => format!("{}", c.paint(t)), fn apply(&self, c: Style) -> Cow<str>;
false => t, }
}
impl<T: AsRef<str>> Applyable for T {
fn apply(&self, c: Style) -> Cow<str> {
let s = self.as_ref();
match USE_COLOR.load(Ordering::Relaxed) {
true => Cow::Owned(format!("{}", c.paint(s))),
false => Cow::Borrowed(s),
}
}
} }
lazy_static! { lazy_static! {

View File

@ -32,8 +32,6 @@ use misc::version;
use crypto::*; use crypto::*;
use sha3::Hashable; use sha3::Hashable;
use rlp::*; use rlp::*;
use log::Colour::White;
use log::paint;
use network::session::{Session, SessionData}; use network::session::{Session, SessionData};
use error::*; use error::*;
use io::*; use io::*;
@ -162,6 +160,8 @@ pub enum NetworkIoMessage<Message> where Message: Send + Sync + Clone {
Disconnect(PeerId), Disconnect(PeerId),
/// Disconnect and temporary disable peer. /// Disconnect and temporary disable peer.
DisablePeer(PeerId), DisablePeer(PeerId),
/// Network has been started with the host as the given enode.
NetworkStarted(String),
/// User message /// User message
User(Message), User(Message),
} }
@ -345,12 +345,13 @@ pub struct Host<Message> where Message: Send + Sync + Clone {
reserved_nodes: RwLock<HashSet<NodeId>>, reserved_nodes: RwLock<HashSet<NodeId>>,
num_sessions: AtomicUsize, num_sessions: AtomicUsize,
stopping: AtomicBool, stopping: AtomicBool,
first_time: AtomicBool,
} }
impl<Message> Host<Message> where Message: Send + Sync + Clone { impl<Message> Host<Message> where Message: Send + Sync + Clone {
/// Create a new instance /// Create a new instance
pub fn new(config: NetworkConfiguration, stats: Arc<NetworkStats>) -> Result<Host<Message>, UtilError> { pub fn new(config: NetworkConfiguration, stats: Arc<NetworkStats>) -> Result<Host<Message>, UtilError> {
trace!(target: "host", "Creating new Host object");
let mut listen_address = match config.listen_address { let mut listen_address = match config.listen_address {
None => SocketAddr::from_str("0.0.0.0:30304").unwrap(), None => SocketAddr::from_str("0.0.0.0:30304").unwrap(),
Some(addr) => addr, Some(addr) => addr,
@ -401,7 +402,6 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
reserved_nodes: RwLock::new(HashSet::new()), reserved_nodes: RwLock::new(HashSet::new()),
num_sessions: AtomicUsize::new(0), num_sessions: AtomicUsize::new(0),
stopping: AtomicBool::new(false), stopping: AtomicBool::new(false),
first_time: AtomicBool::new(true),
}; };
for n in boot_nodes { for n in boot_nodes {
@ -538,9 +538,8 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone()); self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone());
if self.first_time.load(AtomicOrdering::Relaxed) { if let Some(url) = self.external_url() {
info!("Public node URL: {}", paint(White.bold(), self.external_url().unwrap())); io.message(NetworkIoMessage::NetworkStarted(url)).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
self.first_time.store(false, AtomicOrdering::Relaxed);
} }
// Initialize discovery. // Initialize discovery.
@ -1038,6 +1037,7 @@ impl<Message> IoHandler<NetworkIoMessage<Message>> for Host<Message> where Messa
h.message(&NetworkContext::new(io, p, None, self.sessions.clone(), &reserved), &message); h.message(&NetworkContext::new(io, p, None, self.sessions.clone(), &reserved), &message);
} }
} }
_ => {} // ignore others.
} }
} }

View File

@ -78,6 +78,11 @@ impl<Message> NetworkService<Message> where Message: Send + Sync + Clone + 'stat
&self.stats &self.stats
} }
/// Returns network configuration.
pub fn config(&self) -> &NetworkConfiguration {
&self.config
}
/// Returns external url if available. /// Returns external url if available.
pub fn external_url(&self) -> Option<String> { pub fn external_url(&self) -> Option<String> {
let host = self.host.read().unwrap(); let host = self.host.read().unwrap();