Merge branch 'master' into trie

This commit is contained in:
debris 2017-08-28 15:08:22 +02:00
commit 322da07010
51 changed files with 1173 additions and 489 deletions

21
Cargo.lock generated
View File

@ -935,6 +935,7 @@ dependencies = [
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.8.0",
"ethcore-util 1.8.0",
"ethjson 0.1.0",
"evm 0.1.0",
"panic_hook 0.1.0",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1613,6 +1614,21 @@ dependencies = [
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "node-health"
version = "0.1.0"
dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"ntp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0",
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nodrop"
version = "0.1.9"
@ -1821,6 +1837,7 @@ dependencies = [
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"node-health 0.1.0",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"panic_hook 0.1.0",
@ -1862,7 +1879,6 @@ dependencies = [
"ethcore-util 1.8.0",
"fetch 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1870,7 +1886,7 @@ dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ntp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"node-health 0.1.0",
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-hash-fetch 1.8.0",
"parity-reactor 0.1.0",
@ -1985,6 +2001,7 @@ dependencies = [
"jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"node-health 0.1.0",
"order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0",
"parity-updater 1.8.0",

View File

@ -43,6 +43,7 @@ ethcore-logger = { path = "logger" }
ethcore-stratum = { path = "stratum" }
ethcore-network = { path = "util/network" }
ethkey = { path = "ethkey" }
node-health = { path = "dapps/node-health" }
rlp = { path = "util/rlp" }
rpc-cli = { path = "rpc_cli" }
parity-hash-fetch = { path = "hash-fetch" }
@ -110,4 +111,4 @@ lto = false
panic = "abort"
[workspace]
members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec"]
members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec", "dapps/node-health"]

View File

@ -9,15 +9,12 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
base32 = "0.3"
env_logger = "0.4"
futures = "0.1"
futures-cpupool = "0.1"
linked-hash-map = "0.3"
log = "0.3"
parity-dapps-glue = "1.7"
mime = "0.2"
mime_guess = "1.6.1"
ntp = "0.2.0"
rand = "0.3"
rustc-hex = "1.0"
serde = "1.0"
@ -32,15 +29,19 @@ itertools = "0.5"
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
ethcore-devtools = { path = "../devtools" }
ethcore-util = { path = "../util" }
fetch = { path = "../util/fetch" }
node-health = { path = "./node-health" }
parity-hash-fetch = { path = "../hash-fetch" }
parity-reactor = { path = "../util/reactor" }
parity-ui = { path = "./ui" }
clippy = { version = "0.0.103", optional = true}
[dev-dependencies]
env_logger = "0.4"
ethcore-devtools = { path = "../devtools" }
[features]
dev = ["clippy", "ethcore-util/dev"]

View File

@ -0,0 +1,18 @@
[package]
name = "node-health"
description = "Node's health status"
version = "0.1.0"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
futures = "0.1"
futures-cpupool = "0.1"
log = "0.3"
ntp = "0.2.0"
parking_lot = "0.4"
serde = "1.0"
serde_derive = "1.0"
time = "0.1.35"
parity-reactor = { path = "../../util/reactor" }

View File

@ -0,0 +1,122 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Reporting node's health.
use std::sync::Arc;
use std::time;
use futures::{Future, BoxFuture};
use futures::sync::oneshot;
use types::{HealthInfo, HealthStatus, Health};
use time::{TimeChecker, MAX_DRIFT};
use parity_reactor::Remote;
use parking_lot::Mutex;
use {SyncStatus};
const TIMEOUT_SECS: u64 = 5;
const PROOF: &str = "Only one closure is invoked.";
/// A struct enabling you to query for node's health.
#[derive(Debug, Clone)]
pub struct NodeHealth {
sync_status: Arc<SyncStatus>,
time: TimeChecker,
remote: Remote,
}
impl NodeHealth {
/// Creates new `NodeHealth`.
pub fn new(sync_status: Arc<SyncStatus>, time: TimeChecker, remote: Remote) -> Self {
NodeHealth { sync_status, time, remote, }
}
/// Query latest health report.
pub fn health(&self) -> BoxFuture<Health, ()> {
trace!(target: "dapps", "Checking node health.");
// Check timediff
let sync_status = self.sync_status.clone();
let time = self.time.time_drift();
let (tx, rx) = oneshot::channel();
let tx = Arc::new(Mutex::new(Some(tx)));
let tx2 = tx.clone();
self.remote.spawn_with_timeout(
move || time.then(move |result| {
let _ = tx.lock().take().expect(PROOF).send(Ok(result));
Ok(())
}),
time::Duration::from_secs(TIMEOUT_SECS),
move || {
let _ = tx2.lock().take().expect(PROOF).send(Err(()));
},
);
rx.map_err(|err| {
warn!(target: "dapps", "Health request cancelled: {:?}", err);
}).and_then(move |time| {
// Check peers
let peers = {
let (connected, max) = sync_status.peers();
let (status, message) = match connected {
0 => {
(HealthStatus::Bad, "You are not connected to any peers. There is most likely some network issue. Fix connectivity.".into())
},
1 => (HealthStatus::NeedsAttention, "You are connected to only one peer. Your node might not be reliable. Check your network connection.".into()),
_ => (HealthStatus::Ok, "".into()),
};
HealthInfo { status, message, details: (connected, max) }
};
// Check sync
let sync = {
let is_syncing = sync_status.is_major_importing();
let (status, message) = if is_syncing {
(HealthStatus::NeedsAttention, "Your node is still syncing, the values you see might be outdated. Wait until it's fully synced.".into())
} else {
(HealthStatus::Ok, "".into())
};
HealthInfo { status, message, details: is_syncing }
};
// Check time
let time = {
let (status, message, details) = match time {
Ok(Ok(diff)) if diff < MAX_DRIFT && diff > -MAX_DRIFT => {
(HealthStatus::Ok, "".into(), diff)
},
Ok(Ok(diff)) => {
(HealthStatus::Bad, format!(
"Your clock is not in sync. Detected difference is too big for the protocol to work: {}ms. Synchronize your clock.",
diff,
), diff)
},
Ok(Err(err)) => {
(HealthStatus::NeedsAttention, format!(
"Unable to reach time API: {}. Make sure that your clock is synchronized.",
err,
), 0)
},
Err(_) => {
(HealthStatus::NeedsAttention, "Time API request timed out. Make sure that the clock is synchronized.".into(), 0)
},
};
HealthInfo { status, message, details, }
};
Ok(Health { peers, sync, time})
}).boxed()
}
}

View File

@ -0,0 +1,49 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Node Health status reporting.
#![warn(missing_docs)]
extern crate futures;
extern crate futures_cpupool;
extern crate ntp;
extern crate time as time_crate;
extern crate parity_reactor;
extern crate parking_lot;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
mod health;
mod time;
mod types;
pub use futures_cpupool::CpuPool;
pub use health::NodeHealth;
pub use types::{Health, HealthInfo, HealthStatus};
pub use time::{TimeChecker, Error};
/// Indicates sync status
pub trait SyncStatus: ::std::fmt::Debug + Send + Sync {
/// Returns true if there is a major sync happening.
fn is_major_importing(&self) -> bool;
/// Returns number of connected and ideal peers.
fn peers(&self) -> (usize, usize);
}

View File

@ -41,8 +41,8 @@ use futures::{self, Future, BoxFuture};
use futures::future::{self, IntoFuture};
use futures_cpupool::{CpuPool, CpuFuture};
use ntp;
use time::{Duration, Timespec};
use util::RwLock;
use parking_lot::RwLock;
use time_crate::{Duration, Timespec};
/// Time checker error.
#[derive(Debug, Clone, PartialEq)]
@ -164,7 +164,7 @@ impl Ntp for SimpleNtp {
match ntp::request(&server.address) {
Ok(packet) => {
let dest_time = ::time::now_utc().to_timespec();
let dest_time = ::time_crate::now_utc().to_timespec();
let orig_time = Timespec::from(packet.orig_time);
let recv_time = Timespec::from(packet.recv_time);
let transmit_time = Timespec::from(packet.transmit_time);
@ -293,7 +293,7 @@ mod tests {
use time::Duration;
use futures::{future, Future};
use super::{Ntp, TimeChecker, Error};
use util::RwLock;
use parking_lot::RwLock;
#[derive(Clone)]
struct FakeNtp(RefCell<Vec<Duration>>, Cell<u64>);

View File

@ -0,0 +1,57 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Base health types.
/// Health API endpoint status.
#[derive(Debug, PartialEq, Serialize)]
pub enum HealthStatus {
/// Everything's OK.
#[serde(rename = "ok")]
Ok,
/// Node health need attention
/// (the issue is not critical, but may need investigation)
#[serde(rename = "needsAttention")]
NeedsAttention,
/// There is something bad detected with the node.
#[serde(rename = "bad")]
Bad,
}
/// Represents a single check in node health.
/// Cointains the status of that check and apropriate message and details.
#[derive(Debug, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct HealthInfo<T> {
/// Check status.
pub status: HealthStatus,
/// Human-readable message.
pub message: String,
/// Technical details of the check.
pub details: T,
}
/// Node Health status.
#[derive(Debug, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Health {
/// Status of peers.
pub peers: HealthInfo<(usize, usize)>,
/// Sync status.
pub sync: HealthInfo<bool>,
/// Time diff info.
pub time: HealthInfo<i64>,
}

View File

@ -21,32 +21,28 @@ use hyper::method::Method;
use hyper::status::StatusCode;
use api::{response, types};
use api::time::{TimeChecker, MAX_DRIFT};
use apps::fetcher::Fetcher;
use handlers::{self, extract_url};
use endpoint::{Endpoint, Handler, EndpointPath};
use node_health::{NodeHealth, HealthStatus, Health};
use parity_reactor::Remote;
use {SyncStatus};
#[derive(Clone)]
pub struct RestApi {
fetcher: Arc<Fetcher>,
sync_status: Arc<SyncStatus>,
time: TimeChecker,
health: NodeHealth,
remote: Remote,
}
impl RestApi {
pub fn new(
fetcher: Arc<Fetcher>,
sync_status: Arc<SyncStatus>,
time: TimeChecker,
health: NodeHealth,
remote: Remote,
) -> Box<Endpoint> {
Box::new(RestApi {
fetcher,
sync_status,
time,
health,
remote,
})
}
@ -90,68 +86,23 @@ impl RestApiRouter {
}
fn health(&self, control: Control) -> Box<Handler> {
use self::types::{HealthInfo, HealthStatus, Health};
trace!(target: "dapps", "Checking node health.");
// Check timediff
let sync_status = self.api.sync_status.clone();
let map = move |time| {
// Check peers
let peers = {
let (connected, max) = sync_status.peers();
let (status, message) = match connected {
0 => {
(HealthStatus::Bad, "You are not connected to any peers. There is most likely some network issue. Fix connectivity.".into())
},
1 => (HealthStatus::NeedsAttention, "You are connected to only one peer. Your node might not be reliable. Check your network connection.".into()),
_ => (HealthStatus::Ok, "".into()),
};
HealthInfo { status, message, details: (connected, max) }
let map = move |health: Result<Result<Health, ()>, ()>| {
let status = match health {
Ok(Ok(ref health)) => {
if [&health.peers.status, &health.sync.status].iter().any(|x| *x != &HealthStatus::Ok) {
StatusCode::PreconditionFailed // HTTP 412
} else {
StatusCode::Ok // HTTP 200
}
},
_ => StatusCode::ServiceUnavailable, // HTTP 503
};
// Check sync
let sync = {
let is_syncing = sync_status.is_major_importing();
let (status, message) = if is_syncing {
(HealthStatus::NeedsAttention, "Your node is still syncing, the values you see might be outdated. Wait until it's fully synced.".into())
} else {
(HealthStatus::Ok, "".into())
};
HealthInfo { status, message, details: is_syncing }
};
// Check time
let time = {
let (status, message, details) = match time {
Ok(Ok(diff)) if diff < MAX_DRIFT && diff > -MAX_DRIFT => {
(HealthStatus::Ok, "".into(), diff)
},
Ok(Ok(diff)) => {
(HealthStatus::Bad, format!(
"Your clock is not in sync. Detected difference is too big for the protocol to work: {}ms. Synchronize your clock.",
diff,
), diff)
},
Ok(Err(err)) => {
(HealthStatus::NeedsAttention, format!(
"Unable to reach time API: {}. Make sure that your clock is synchronized.",
err,
), 0)
},
Err(_) => {
(HealthStatus::NeedsAttention, "Time API request timed out. Make sure that the clock is synchronized.".into(), 0)
},
};
HealthInfo { status, message, details, }
};
response::as_json(StatusCode::Ok, &Health { peers, sync, time })
response::as_json(status, &health)
};
let time = self.api.time.time_drift();
let health = self.api.health.health();
let remote = self.api.remote.clone();
Box::new(handlers::AsyncHandler::new(time, map, remote, control))
Box::new(handlers::AsyncHandler::new(health, map, remote, control))
}
}

View File

@ -18,8 +18,6 @@
mod api;
mod response;
mod time;
mod types;
pub use self::api::RestApi;
pub use self::time::TimeChecker;

View File

@ -25,43 +25,3 @@ pub struct ApiError {
/// More technical error details.
pub detail: String,
}
/// Health API endpoint status.
#[derive(Debug, PartialEq, Serialize)]
pub enum HealthStatus {
/// Everything's OK.
#[serde(rename = "ok")]
Ok,
/// Node health need attention
/// (the issue is not critical, but may need investigation)
#[serde(rename = "needsAttention")]
NeedsAttention,
/// There is something bad detected with the node.
#[serde(rename = "bad")]
Bad
}
/// Represents a single check in node health.
/// Cointains the status of that check and apropriate message and details.
#[derive(Debug, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct HealthInfo<T> {
/// Check status.
pub status: HealthStatus,
/// Human-readable message.
pub message: String,
/// Technical details of the check.
pub details: T,
}
/// Node Health status.
#[derive(Debug, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Health {
/// Status of peers.
pub peers: HealthInfo<(usize, usize)>,
/// Sync status.
pub sync: HealthInfo<bool>,
/// Time diff info.
pub time: HealthInfo<i64>,
}

View File

@ -281,6 +281,7 @@ mod tests {
}
}
#[derive(Debug)]
struct FakeSync(bool);
impl SyncStatus for FakeSync {
fn is_major_importing(&self) -> bool { self.0 }

View File

@ -21,11 +21,9 @@
extern crate base32;
extern crate futures;
extern crate futures_cpupool;
extern crate itertools;
extern crate linked_hash_map;
extern crate mime_guess;
extern crate ntp;
extern crate rand;
extern crate rustc_hex;
extern crate serde;
@ -40,6 +38,7 @@ extern crate jsonrpc_http_server;
extern crate ethcore_util as util;
extern crate fetch;
extern crate node_health;
extern crate parity_dapps_glue as parity_dapps;
extern crate parity_hash_fetch as hash_fetch;
extern crate parity_reactor;
@ -57,7 +56,6 @@ extern crate ethcore_devtools as devtools;
#[cfg(test)]
extern crate env_logger;
mod endpoint;
mod apps;
mod page;
@ -79,19 +77,12 @@ use util::RwLock;
use jsonrpc_http_server::{self as http, hyper, Origin};
use fetch::Fetch;
use futures_cpupool::CpuPool;
use node_health::NodeHealth;
use parity_reactor::Remote;
pub use hash_fetch::urlhint::ContractClient;
pub use node_health::SyncStatus;
/// Indicates sync status
pub trait SyncStatus: Send + Sync {
/// Returns true if there is a major sync happening.
fn is_major_importing(&self) -> bool;
/// Returns number of connected and ideal peers.
fn peers(&self) -> (usize, usize);
}
/// Validates Web Proxy tokens
pub trait WebProxyTokens: Send + Sync {
@ -156,8 +147,7 @@ impl Middleware {
/// Creates new middleware for UI server.
pub fn ui<F: Fetch>(
ntp_servers: &[String],
pool: CpuPool,
health: NodeHealth,
remote: Remote,
dapps_domain: &str,
registrar: Arc<ContractClient>,
@ -172,11 +162,9 @@ impl Middleware {
).embeddable_on(None).allow_dapps(false));
let special = {
let mut special = special_endpoints(
ntp_servers,
pool,
health,
content_fetcher.clone(),
remote.clone(),
sync_status.clone(),
);
special.insert(router::SpecialEndpoint::Home, Some(apps::ui()));
special
@ -197,8 +185,7 @@ impl Middleware {
/// Creates new Dapps server middleware.
pub fn dapps<F: Fetch>(
ntp_servers: &[String],
pool: CpuPool,
health: NodeHealth,
remote: Remote,
ui_address: Option<(String, u16)>,
extra_embed_on: Vec<(String, u16)>,
@ -236,11 +223,9 @@ impl Middleware {
let special = {
let mut special = special_endpoints(
ntp_servers,
pool,
health,
content_fetcher.clone(),
remote.clone(),
sync_status,
);
special.insert(
router::SpecialEndpoint::Home,
@ -270,20 +255,17 @@ impl http::RequestMiddleware for Middleware {
}
}
fn special_endpoints<T: AsRef<str>>(
ntp_servers: &[T],
pool: CpuPool,
fn special_endpoints(
health: NodeHealth,
content_fetcher: Arc<apps::fetcher::Fetcher>,
remote: Remote,
sync_status: Arc<SyncStatus>,
) -> HashMap<router::SpecialEndpoint, Option<Box<endpoint::Endpoint>>> {
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, None);
special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(
content_fetcher,
sync_status,
api::TimeChecker::new(ntp_servers, pool),
health,
remote,
)));
special

View File

@ -26,7 +26,7 @@ use jsonrpc_http_server::{self as http, Host, DomainsValidation};
use devtools::http_client;
use hash_fetch::urlhint::ContractClient;
use fetch::{Fetch, Client as FetchClient};
use futures_cpupool::CpuPool;
use node_health::{NodeHealth, TimeChecker, CpuPool};
use parity_reactor::Remote;
use {Middleware, SyncStatus, WebProxyTokens};
@ -39,6 +39,7 @@ use self::fetch::FakeFetch;
const SIGNER_PORT: u16 = 18180;
#[derive(Debug)]
struct FakeSync(bool);
impl SyncStatus for FakeSync {
fn is_major_importing(&self) -> bool { self.0 }
@ -254,9 +255,13 @@ impl Server {
remote: Remote,
fetch: F,
) -> Result<Server, http::Error> {
let health = NodeHealth::new(
sync_status.clone(),
TimeChecker::new::<String>(&[], CpuPool::new(1)),
remote.clone(),
);
let middleware = Middleware::dapps(
&["0.pool.ntp.org:123".into(), "1.pool.ntp.org:123".into()],
CpuPool::new(4),
health,
remote,
signer_address,
vec![],

@ -1 +1 @@
Subproject commit 85e76c5ea2a54c6c54e35014643b5080a50460c5
Subproject commit 519b0b967cffd7d1236ef21698b1e6e415a048e9

View File

@ -748,7 +748,7 @@ impl Client {
self.factories.clone(),
).expect("state known to be available for just-imported block; qed");
let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
let options = TransactOptions::with_no_tracing().dont_check_nonce();
let res = Executive::new(&mut state, &env_info, &*self.engine)
.transact(&transaction, options);
@ -1113,18 +1113,39 @@ impl Client {
}.fake_sign(from)
}
fn do_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
fn do_virtual_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
fn call<E, V, T>(
state: &mut State<StateDB>,
env_info: &EnvInfo,
engine: &E,
state_diff: bool,
transaction: &SignedTransaction,
options: TransactOptions<T, V>,
) -> Result<Executed, CallError> where
E: Engine + ?Sized,
T: trace::Tracer,
V: trace::VMTracer,
{
let options = options.dont_check_nonce();
let original_state = if state_diff { Some(state.clone()) } else { None };
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
let mut ret = Executive::new(state, env_info, &*self.engine).transact_virtual(t, options)?;
let mut ret = Executive::new(state, env_info, engine).transact_virtual(transaction, options)?;
// TODO gav move this into Executive.
if let Some(original) = original_state {
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
if let Some(original) = original_state {
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
}
Ok(ret)
}
Ok(ret)
let state_diff = analytics.state_diffing;
let engine = &*self.engine;
match (analytics.transaction_tracing, analytics.vm_tracing) {
(true, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing_and_vm_tracing()),
(true, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_tracing()),
(false, true) => call(state, env_info, engine, state_diff, t, TransactOptions::with_vm_tracing()),
(false, false) => call(state, env_info, engine, state_diff, t, TransactOptions::with_no_tracing()),
}
}
}
@ -1157,7 +1178,7 @@ impl BlockChainClient for Client {
// that's just a copy of the state.
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
self.do_call(&env_info, &mut state, transaction, analytics)
self.do_virtual_call(&env_info, &mut state, transaction, analytics)
}
fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError> {
@ -1169,7 +1190,7 @@ impl BlockChainClient for Client {
let mut results = Vec::with_capacity(transactions.len());
for &(ref t, analytics) in transactions {
let ret = self.do_call(&env_info, &mut state, t, analytics)?;
let ret = self.do_virtual_call(&env_info, &mut state, t, analytics)?;
env_info.gas_used = ret.cumulative_gas_used;
results.push(ret);
}
@ -1189,7 +1210,7 @@ impl BlockChainClient for Client {
// that's just a copy of the state.
let original_state = self.state_at(block).ok_or(CallError::StatePruned)?;
let sender = t.sender();
let options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false };
let options = || TransactOptions::with_tracing();
let cond = |gas| {
let mut tx = t.as_unsigned().clone();
@ -1198,7 +1219,7 @@ impl BlockChainClient for Client {
let mut state = original_state.clone();
Ok(Executive::new(&mut state, &env_info, &*self.engine)
.transact_virtual(&tx, options.clone())
.transact_virtual(&tx, options())
.map(|r| r.exception.is_none())
.unwrap_or(false))
};
@ -1254,22 +1275,17 @@ impl BlockChainClient for Client {
return Err(CallError::TransactionNotFound);
}
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed";
let rest = txs.split_off(address.index);
for t in txs {
let t = SignedTransaction::new(t).expect(PROOF);
let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, Default::default())?;
let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, TransactOptions::with_no_tracing())?;
env_info.gas_used = env_info.gas_used + x.gas_used;
}
let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
let t = SignedTransaction::new(first).expect(PROOF);
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
let mut ret = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, options)?;
if let Some(original) = original_state {
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?)
}
Ok(ret)
self.do_virtual_call(&env_info, &mut state, &t, analytics)
}
fn mode(&self) -> IpcMode {
@ -1951,7 +1967,7 @@ impl ProvingBlockChainClient for Client {
let backend = state::backend::Proving::new(jdb.as_hashdb_mut());
let mut state = state.replace_backend(backend);
let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
let options = TransactOptions::with_no_tracing().dont_check_nonce();
let res = Executive::new(&mut state, &env_info, &*self.engine).transact(&transaction, options);
match res {

View File

@ -18,9 +18,9 @@
use std::fmt;
use std::sync::Arc;
use util::{self, U256, journaldb, trie};
use util::{self, U256, H256, journaldb, trie};
use util::kvdb::{self, KeyValueDB};
use {state, state_db, client, executive, trace, db, spec};
use {state, state_db, client, executive, trace, transaction, db, spec, pod_state};
use factory::Factories;
use evm::{self, VMType};
use vm::{self, ActionParams};
@ -33,9 +33,17 @@ pub enum EvmTestError {
/// EVM error.
Evm(vm::Error),
/// Initialization error.
Initialization(::error::Error),
ClientError(::error::Error),
/// Low-level database error.
Database(String),
/// Post-condition failure,
PostCondition(String),
}
impl<E: Into<::error::Error>> From<E> for EvmTestError {
fn from(err: E) -> Self {
EvmTestError::ClientError(err.into())
}
}
impl fmt::Display for EvmTestError {
@ -45,52 +53,114 @@ impl fmt::Display for EvmTestError {
match *self {
Trie(ref err) => write!(fmt, "Trie: {}", err),
Evm(ref err) => write!(fmt, "EVM: {}", err),
Initialization(ref err) => write!(fmt, "Initialization: {}", err),
ClientError(ref err) => write!(fmt, "{}", err),
Database(ref err) => write!(fmt, "DB: {}", err),
PostCondition(ref err) => write!(fmt, "{}", err),
}
}
}
/// Simplified, single-block EVM test client.
pub struct EvmTestClient {
state_db: state_db::StateDB,
factories: Factories,
spec: spec::Spec,
use ethereum;
use ethjson::state::test::ForkSpec;
lazy_static! {
pub static ref FRONTIER: spec::Spec = ethereum::new_frontier_test();
pub static ref HOMESTEAD: spec::Spec = ethereum::new_homestead_test();
pub static ref EIP150: spec::Spec = ethereum::new_eip150_test();
pub static ref EIP161: spec::Spec = ethereum::new_eip161_test();
pub static ref _METROPOLIS: spec::Spec = ethereum::new_metropolis_test();
}
impl EvmTestClient {
/// Creates new EVM test client with in-memory DB initialized with genesis of given Spec.
pub fn new(spec: spec::Spec) -> Result<Self, EvmTestError> {
let factories = Factories {
vm: evm::Factory::new(VMType::Interpreter, 5 * 1024),
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
accountdb: Default::default(),
};
let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed")));
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE);
let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024);
state_db = spec.ensure_db_good(state_db, &factories).map_err(EvmTestError::Initialization)?;
// Write DB
{
let mut batch = kvdb::DBTransaction::new();
state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()).map_err(|e| EvmTestError::Initialization(e.into()))?;
db.write(batch).map_err(EvmTestError::Database)?;
/// Simplified, single-block EVM test client.
pub struct EvmTestClient<'a> {
state: state::State<state_db::StateDB>,
spec: &'a spec::Spec,
}
impl<'a> EvmTestClient<'a> {
/// Converts a json spec definition into spec.
pub fn spec_from_json(spec: &ForkSpec) -> Option<&'static spec::Spec> {
match *spec {
ForkSpec::Frontier => Some(&*FRONTIER),
ForkSpec::Homestead => Some(&*HOMESTEAD),
ForkSpec::EIP150 => Some(&*EIP150),
ForkSpec::EIP158 => Some(&*EIP161),
ForkSpec::Metropolis | ForkSpec::Byzantium | ForkSpec::Constantinople => None,
}
}
/// Creates new EVM test client with in-memory DB initialized with genesis of given Spec.
pub fn new(spec: &'a spec::Spec) -> Result<Self, EvmTestError> {
let factories = Self::factories();
let state = Self::state_from_spec(spec, &factories)?;
Ok(EvmTestClient {
state_db,
factories,
state,
spec,
})
}
/// Call given contract.
/// Creates new EVM test client with in-memory DB initialized with given PodState.
pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result<Self, EvmTestError> {
let factories = Self::factories();
let state = Self::state_from_pod(spec, &factories, pod_state)?;
Ok(EvmTestClient {
state,
spec,
})
}
fn factories() -> Factories {
Factories {
vm: evm::Factory::new(VMType::Interpreter, 5 * 1024),
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
accountdb: Default::default(),
}
}
fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result<state::State<state_db::StateDB>, EvmTestError> {
let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed")));
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE);
let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024);
state_db = spec.ensure_db_good(state_db, factories)?;
let genesis = spec.genesis_header();
// Write DB
{
let mut batch = kvdb::DBTransaction::new();
state_db.journal_under(&mut batch, 0, &genesis.hash())?;
db.write(batch).map_err(EvmTestError::Database)?;
}
state::State::from_existing(
state_db,
*genesis.state_root(),
spec.engine.account_start_nonce(0),
factories.clone()
).map_err(EvmTestError::Trie)
}
fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: pod_state::PodState) -> Result<state::State<state_db::StateDB>, EvmTestError> {
let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed")));
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE);
let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024);
let mut state = state::State::new(
state_db,
spec.engine.account_start_nonce(0),
factories.clone(),
);
state.populate_from(pod_state);
state.commit()?;
Ok(state)
}
/// Execute the VM given ActionParams and tracer.
/// Returns amount of gas left and the output.
pub fn call<T: trace::VMTracer>(&mut self, params: ActionParams, vm_tracer: &mut T)
-> Result<(U256, Vec<u8>), EvmTestError>
{
let genesis = self.spec.genesis_header();
let mut state = state::State::from_existing(self.state_db.boxed_clone(), *genesis.state_root(), self.spec.engine.account_start_nonce(0), self.factories.clone())
.map_err(EvmTestError::Trie)?;
let info = client::EnvInfo {
number: genesis.number(),
author: *genesis.author(),
@ -103,7 +173,7 @@ impl EvmTestClient {
let mut substate = state::Substate::new();
let mut tracer = trace::NoopTracer;
let mut output = vec![];
let mut executive = executive::Executive::new(&mut state, &info, &*self.spec.engine);
let mut executive = executive::Executive::new(&mut self.state, &info, &*self.spec.engine);
let (gas_left, _) = executive.call(
params,
&mut substate,
@ -114,4 +184,59 @@ impl EvmTestClient {
Ok((gas_left, output))
}
/// Executes a SignedTransaction within context of the provided state and `EnvInfo`.
/// Returns the state root, gas left and the output.
pub fn transact<T: trace::VMTracer>(
&mut self,
env_info: &client::EnvInfo,
transaction: transaction::SignedTransaction,
vm_tracer: T,
) -> TransactResult {
let initial_gas = transaction.gas;
// Verify transaction
let is_ok = transaction.verify_basic(true, None, env_info.number >= self.spec.engine.params().eip86_transition);
if let Err(error) = is_ok {
return TransactResult::Err {
state_root: *self.state.root(),
error,
};
}
// Apply transaction
let tracer = trace::NoopTracer;
let result = self.state.apply_with_tracing(&env_info, &*self.spec.engine, &transaction, tracer, vm_tracer);
match result {
Ok(result) => TransactResult::Ok {
state_root: *self.state.root(),
gas_left: initial_gas - result.receipt.gas_used,
output: result.output
},
Err(error) => TransactResult::Err {
state_root: *self.state.root(),
error,
},
}
}
}
/// A result of applying transaction to the state.
pub enum TransactResult {
/// Successful execution
Ok {
/// State root
state_root: H256,
/// Amount of gas left
gas_left: U256,
/// Output
output: Vec<u8>,
},
/// Transaction failed to run
Err {
/// State root
state_root: H256,
/// Execution error
error: ::error::Error,
},
}

View File

@ -27,7 +27,7 @@ mod client;
pub use self::client::*;
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType};
pub use self::error::Error;
pub use self::evm_test_client::{EvmTestClient, EvmTestError};
pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactResult};
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::chain_notify::ChainNotify;
pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient};

View File

@ -187,7 +187,14 @@ impl Engine for Arc<Ethash> {
/// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, header: &Header) -> BTreeMap<String, String> {
map!["nonce".to_owned() => format!("0x{}", header.nonce().hex()), "mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())]
if header.seal().len() == self.seal_fields() {
map![
"nonce".to_owned() => format!("0x{}", header.nonce().hex()),
"mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())
]
} else {
BTreeMap::default()
}
}
fn schedule(&self, block_number: BlockNumber) -> Schedule {

View File

@ -26,7 +26,7 @@ use evm::{CallType, Factory, Finalize, FinalizationResult};
use vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue};
use wasm;
use externalities::*;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use trace::{self, FlatTrace, VMTrace, Tracer, VMTracer};
use transaction::{Action, SignedTransaction};
use crossbeam;
pub use executed::{Executed, ExecutionResult};
@ -66,16 +66,77 @@ pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address,
}
/// Transaction execution options.
#[derive(Default, Copy, Clone, PartialEq)]
pub struct TransactOptions {
#[derive(Copy, Clone, PartialEq)]
pub struct TransactOptions<T, V> {
/// Enable call tracing.
pub tracing: bool,
pub tracer: T,
/// Enable VM tracing.
pub vm_tracing: bool,
pub vm_tracer: V,
/// Check transaction nonce before execution.
pub check_nonce: bool,
}
impl<T, V> TransactOptions<T, V> {
/// Create new `TransactOptions` with given tracer and VM tracer.
pub fn new(tracer: T, vm_tracer: V) -> Self {
TransactOptions {
tracer,
vm_tracer,
check_nonce: true,
}
}
/// Disables the nonce check
pub fn dont_check_nonce(mut self) -> Self {
self.check_nonce = false;
self
}
}
impl TransactOptions<trace::ExecutiveTracer, trace::ExecutiveVMTracer> {
/// Creates new `TransactOptions` with default tracing and VM tracing.
pub fn with_tracing_and_vm_tracing() -> Self {
TransactOptions {
tracer: trace::ExecutiveTracer::default(),
vm_tracer: trace::ExecutiveVMTracer::toplevel(),
check_nonce: true,
}
}
}
impl TransactOptions<trace::ExecutiveTracer, trace::NoopVMTracer> {
/// Creates new `TransactOptions` with default tracing and no VM tracing.
pub fn with_tracing() -> Self {
TransactOptions {
tracer: trace::ExecutiveTracer::default(),
vm_tracer: trace::NoopVMTracer,
check_nonce: true,
}
}
}
impl TransactOptions<trace::NoopTracer, trace::ExecutiveVMTracer> {
/// Creates new `TransactOptions` with no tracing and default VM tracing.
pub fn with_vm_tracing() -> Self {
TransactOptions {
tracer: trace::NoopTracer,
vm_tracer: trace::ExecutiveVMTracer::toplevel(),
check_nonce: true,
}
}
}
impl TransactOptions<trace::NoopTracer, trace::NoopVMTracer> {
/// Creates new `TransactOptions` without any tracing.
pub fn with_no_tracing() -> Self {
TransactOptions {
tracer: trace::NoopTracer,
vm_tracer: trace::NoopVMTracer,
check_nonce: true,
}
}
}
pub fn executor<E>(engine: &E, vm_factory: &Factory, params: &ActionParams)
-> Box<vm::Vm> where E: Engine + ?Sized
{
@ -137,24 +198,18 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
}
/// This function should be used to execute transaction.
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
let check = options.check_nonce;
match options.tracing {
true => match options.vm_tracing {
true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::toplevel()),
false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer),
},
false => match options.vm_tracing {
true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::toplevel()),
false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer),
},
}
pub fn transact<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
{
self.transact_with_tracer(t, options.check_nonce, options.tracer, options.vm_tracer)
}
/// Execute a transaction in a "virtual" context.
/// This will ensure the caller has enough balance to execute the desired transaction.
/// Used for extra-block executions for things like consensus contracts and RPCs
pub fn transact_virtual(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
pub fn transact_virtual<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
{
let sender = t.sender();
let balance = self.state.balance(&sender)?;
let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price));
@ -167,7 +222,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
}
/// Execute transaction/call with tracing enabled
pub fn transact_with_tracer<T, V>(
fn transact_with_tracer<T, V>(
&'a mut self,
t: &SignedTransaction,
check_nonce: bool,
@ -261,7 +316,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
};
// finalize here!
Ok(self.finalize(t, substate, result, output, tracer.traces(), vm_tracer.drain())?)
Ok(self.finalize(t, substate, result, output, tracer.drain(), vm_tracer.drain())?)
}
fn exec_vm<T, V>(
@ -399,7 +454,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
trace!(target: "executive", "res={:?}", res);
let traces = subtracer.traces();
let traces = subtracer.drain();
match res {
Ok(ref res) => tracer.trace_call(
trace_info,
@ -484,9 +539,9 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
gas - res.gas_left,
trace_output,
created,
subtracer.traces()
subtracer.drain()
),
Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.traces(), e.into())
Err(ref e) => tracer.trace_failed_create(trace_info, subtracer.drain(), e.into())
};
self.enact_result(&res, substate, unconfirmed_substate);
@ -794,7 +849,7 @@ mod tests {
}),
}];
assert_eq!(tracer.traces(), expected_trace);
assert_eq!(tracer.drain(), expected_trace);
let expected_vm_trace = VMTrace {
parent_step: 0,
@ -887,7 +942,7 @@ mod tests {
}),
}];
assert_eq!(tracer.traces(), expected_trace);
assert_eq!(tracer.drain(), expected_trace);
let expected_vm_trace = VMTrace {
parent_step: 0,
@ -1138,7 +1193,7 @@ mod tests {
let executed = {
let mut ex = Executive::new(&mut state, &info, &engine);
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
let opts = TransactOptions::with_no_tracing();
ex.transact(&t, opts).unwrap()
};
@ -1175,7 +1230,7 @@ mod tests {
let res = {
let mut ex = Executive::new(&mut state, &info, &engine);
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
let opts = TransactOptions::with_no_tracing();
ex.transact(&t, opts)
};
@ -1208,7 +1263,7 @@ mod tests {
let res = {
let mut ex = Executive::new(&mut state, &info, &engine);
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
let opts = TransactOptions::with_no_tracing();
ex.transact(&t, opts)
};
@ -1241,7 +1296,7 @@ mod tests {
let res = {
let mut ex = Executive::new(&mut state, &info, &engine);
let opts = TransactOptions { check_nonce: true, tracing: false, vm_tracing: false };
let opts = TransactOptions::with_no_tracing();
ex.transact(&t, opts)
};

View File

@ -15,23 +15,13 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use super::test_common::*;
use tests::helpers::*;
use pod_state::PodState;
use ethereum;
use spec::Spec;
use trace;
use client::{EvmTestClient, EvmTestError, TransactResult};
use ethjson;
use ethjson::state::test::ForkSpec;
use transaction::SignedTransaction;
use vm::EnvInfo;
lazy_static! {
pub static ref FRONTIER: Spec = ethereum::new_frontier_test();
pub static ref HOMESTEAD: Spec = ethereum::new_homestead_test();
pub static ref EIP150: Spec = ethereum::new_eip150_test();
pub static ref EIP161: Spec = ethereum::new_eip161_test();
pub static ref _METROPOLIS: Spec = ethereum::new_metropolis_test();
}
pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
::ethcore_logger::init_log();
let tests = ethjson::state::test::Test::load(json_data).unwrap();
@ -43,35 +33,49 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
let env: EnvInfo = test.env.into();
let pre: PodState = test.pre_state.into();
for (spec, states) in test.post_states {
for (spec_name, states) in test.post_states {
let total = states.len();
let engine = match spec {
ForkSpec::Frontier => &FRONTIER.engine,
ForkSpec::Homestead => &HOMESTEAD.engine,
ForkSpec::EIP150 => &EIP150.engine,
ForkSpec::EIP158 => &EIP161.engine,
ForkSpec::Metropolis => continue,
let spec = match EvmTestClient::spec_from_json(&spec_name) {
Some(spec) => spec,
None => {
println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name);
continue;
}
};
for (i, state) in states.into_iter().enumerate() {
let info = format!(" - {} | {:?} ({}/{}) ...", name, spec, i + 1, total);
let info = format!(" - {} | {:?} ({}/{}) ...", name, spec_name, i + 1, total);
let post_root: H256 = state.hash.into();
let transaction: SignedTransaction = multitransaction.select(&state.indexes).into();
let mut state = get_temp_state();
state.populate_from(pre.clone());
if transaction.verify_basic(true, None, env.number >= engine.params().eip86_transition).is_ok() {
state.commit().expect(&format!("State test {} failed due to internal error.", name));
let _res = state.apply(&env, &**engine, &transaction, false);
} else {
let _rest = state.commit();
}
if state.root() != &post_root {
println!("{} !!! State mismatch (got: {}, expect: {}", info, state.root(), post_root);
flushln!("{} fail", info);
failed.push(name.clone());
} else {
flushln!("{} ok", info);
let result = || -> Result<_, EvmTestError> {
Ok(EvmTestClient::from_pod_state(spec, pre.clone())?
.transact(&env, transaction, trace::NoopVMTracer))
};
match result() {
Err(err) => {
println!("{} !!! Unexpected internal error: {:?}", info, err);
flushln!("{} fail", info);
failed.push(name.clone());
},
Ok(TransactResult::Ok { state_root, .. }) if state_root != post_root => {
println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root);
flushln!("{} fail", info);
failed.push(name.clone());
},
Ok(TransactResult::Err { state_root, ref error }) if state_root != post_root => {
println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root);
println!("{} !!! Execution error: {:?}", info, error);
flushln!("{} fail", info);
failed.push(name.clone());
},
Ok(TransactResult::Err { error, .. }) => {
flushln!("{} ok ({:?})", info, error);
},
Ok(_) => {
flushln!("{} ok", info);
},
}
}
}

View File

@ -31,7 +31,7 @@ use vm::EnvInfo;
use error::Error;
use executive::{Executive, TransactOptions};
use factory::Factories;
use trace::FlatTrace;
use trace::{self, FlatTrace, VMTrace};
use pod_account::*;
use pod_state::{self, PodState};
use types::basic_account::BasicAccount;
@ -59,8 +59,12 @@ pub use self::substate::Substate;
pub struct ApplyOutcome {
/// The receipt for the applied transaction.
pub receipt: Receipt,
/// The trace for the applied transaction, if None if tracing is disabled.
/// The output of the applied transaction.
pub output: Bytes,
/// The trace for the applied transaction, empty if tracing was not produced.
pub trace: Vec<FlatTrace>,
/// The VM trace for the applied transaction, None if tracing was not produced.
pub vm_trace: Option<VMTrace>
}
/// Result type for the execution ("application") of a transaction.
@ -205,7 +209,7 @@ pub fn check_proof(
Err(_) => return ProvedExecution::BadProof,
};
match state.execute(env_info, engine, transaction, false, true) {
match state.execute(env_info, engine, transaction, TransactOptions::with_no_tracing(), true) {
Ok(executed) => ProvedExecution::Complete(executed),
Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof,
Err(e) => ProvedExecution::Failed(e),
@ -290,7 +294,7 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v
impl<B: Backend> State<B> {
/// Creates new state with empty state root
#[cfg(test)]
/// Used for tests.
pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State<B> {
let mut root = H256::new();
{
@ -623,29 +627,57 @@ impl<B: Backend> State<B> {
/// Execute a given transaction, producing a receipt and an optional trace.
/// This will change the state accordingly.
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
// let old = self.to_pod();
if tracing {
let options = TransactOptions::with_tracing();
self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer)
} else {
let options = TransactOptions::with_no_tracing();
self.apply_with_tracing(env_info, engine, t, options.tracer, options.vm_tracer)
}
}
/// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace.
/// This will change the state accordingly.
pub fn apply_with_tracing<V, T>(
&mut self,
env_info: &EnvInfo,
engine: &Engine,
t: &SignedTransaction,
tracer: T,
vm_tracer: V,
) -> ApplyResult where
T: trace::Tracer,
V: trace::VMTracer,
{
let options = TransactOptions::new(tracer, vm_tracer);
let e = self.execute(env_info, engine, t, options, false)?;
let e = self.execute(env_info, engine, t, tracing, false)?;
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
let state_root = if env_info.number < engine.params().eip98_transition || env_info.number < engine.params().validate_receipts_transition {
self.commit()?;
Some(self.root().clone())
} else {
None
};
let output = e.output;
let receipt = Receipt::new(state_root, e.cumulative_gas_used, e.logs);
trace!(target: "state", "Transaction receipt: {:?}", receipt);
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
Ok(ApplyOutcome {
receipt,
output,
trace: e.trace,
vm_trace: e.vm_trace,
})
}
// Execute a given transaction without committing changes.
//
// `virt` signals that we are executing outside of a block set and restrictions like
// gas limits and gas costs should be lifted.
fn execute(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool, virt: bool)
-> Result<Executed, ExecutionError>
fn execute<T, V>(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, options: TransactOptions<T, V>, virt: bool)
-> Result<Executed, ExecutionError> where T: trace::Tracer, V: trace::VMTracer,
{
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
let mut e = Executive::new(self, env_info, engine);
match virt {
@ -730,9 +762,8 @@ impl<B: Backend> State<B> {
Ok(())
}
#[cfg(test)]
#[cfg(feature = "json-tests")]
/// Populate the state from `accounts`.
/// Used for tests.
pub fn populate_from(&mut self, accounts: PodState) {
assert!(self.checkpoints.borrow().is_empty());
for (add, acc) in accounts.drain().into_iter() {

View File

@ -19,7 +19,7 @@ use std::sync::Arc;
use io::IoChannel;
use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockId};
use state::{self, State, CleanupMode};
use executive::Executive;
use executive::{Executive, TransactOptions};
use ethereum;
use block::IsBlock;
use tests::helpers::*;
@ -361,7 +361,7 @@ fn transaction_proof() {
let mut state = State::from_existing(backend, root, 0.into(), factories.clone()).unwrap();
Executive::new(&mut state, &client.latest_env_info(), &*test_spec.engine)
.transact(&transaction, Default::default()).unwrap();
.transact(&transaction, TransactOptions::with_no_tracing().dont_check_nonce()).unwrap();
assert_eq!(state.balance(&Address::default()).unwrap(), 5.into());
assert_eq!(state.balance(&address).unwrap(), 95.into());

View File

@ -167,7 +167,7 @@ impl Tracer for ExecutiveTracer {
ExecutiveTracer::default()
}
fn traces(self) -> Vec<FlatTrace> {
fn drain(self) -> Vec<FlatTrace> {
self.traces
}
}

View File

@ -85,7 +85,7 @@ pub trait Tracer: Send {
fn subtracer(&self) -> Self where Self: Sized;
/// Consumes self and returns all traces.
fn traces(self) -> Vec<FlatTrace>;
fn drain(self) -> Vec<FlatTrace>;
}
/// Used by executive to build VM traces.

View File

@ -62,7 +62,7 @@ impl Tracer for NoopTracer {
NoopTracer
}
fn traces(self) -> Vec<FlatTrace> {
fn drain(self) -> Vec<FlatTrace> {
vec![]
}
}

View File

@ -415,36 +415,121 @@ fn storage_read() {
assert_eq!(Address::from(&result[12..32]), address);
}
macro_rules! reqrep_test {
($name: expr, $input: expr) => {
{
::ethcore_logger::init_log();
let code = load_sample!($name);
// Tests that contract's ability to read from a storage
// Test prepopulates address into storage, than executes a contract which read that address from storage and write this address into result
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
params.data = Some($input);
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut FakeExt::new()).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("Test is expected to return payload to check"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
(gas_left, result)
}
}
}
// math_* tests check the ability of wasm contract to perform big integer operations
// - addition
// - multiplication
// - substraction
// - division
// addition
#[test]
fn math_add() {
::ethcore_logger::init_log();
let code = load_sample!("math.wasm");
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
let mut args = [0u8; 64];
let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap();
let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap();
arg_a.to_big_endian(&mut args[0..32]);
arg_b.to_big_endian(&mut args[32..64]);
params.data = Some(args.to_vec());
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut FakeExt::new()).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("storage_read should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
let (gas_left, result) = reqrep_test!(
"math.wasm",
{
let mut args = [0u8; 65];
let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap();
let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap();
arg_a.to_big_endian(&mut args[1..33]);
arg_b.to_big_endian(&mut args[33..65]);
args.to_vec()
}
};
);
let sum: U256 = (&result[..]).into();
assert_eq!(gas_left, U256::from(96284));
assert_eq!(sum, U256::from_dec_str("1888888888888888888888888888887").unwrap());
assert_eq!(gas_left, U256::from(98087));
assert_eq!(
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
(&result[..]).into()
);
}
// multiplication
#[test]
fn math_mul() {
let (gas_left, result) = reqrep_test!(
"math.wasm",
{
let mut args = [1u8; 65];
let arg_a = U256::from_dec_str("888888888888888888888888888888").unwrap();
let arg_b = U256::from_dec_str("999999999999999999999999999999").unwrap();
arg_a.to_big_endian(&mut args[1..33]);
arg_b.to_big_endian(&mut args[33..65]);
args.to_vec()
}
);
assert_eq!(gas_left, U256::from(97236));
assert_eq!(
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
(&result[..]).into()
);
}
// substraction
#[test]
fn math_sub() {
let (gas_left, result) = reqrep_test!(
"math.wasm",
{
let mut args = [2u8; 65];
let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap();
let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap();
arg_a.to_big_endian(&mut args[1..33]);
arg_b.to_big_endian(&mut args[33..65]);
args.to_vec()
}
);
assert_eq!(gas_left, U256::from(98131));
assert_eq!(
U256::from_dec_str("111111111111111111111111111111").unwrap(),
(&result[..]).into()
);
}
#[test]
fn math_div() {
let (gas_left, result) = reqrep_test!(
"math.wasm",
{
let mut args = [3u8; 65];
let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap();
let arg_b = U256::from_dec_str("888888888888888888888888").unwrap();
arg_a.to_big_endian(&mut args[1..33]);
arg_b.to_big_endian(&mut args[33..65]);
args.to_vec()
}
);
assert_eq!(gas_left, U256::from(91420));
assert_eq!(
U256::from_dec_str("1125000").unwrap(),
(&result[..]).into()
);
}

View File

@ -14,6 +14,7 @@ docopt = "0.8"
serde = "1.0"
serde_derive = "1.0"
ethcore = { path = "../ethcore" }
ethjson = { path = "../json" }
ethcore-util = { path = "../util" }
evm = { path = "../ethcore/evm" }
vm = { path = "../ethcore/vm" }

View File

@ -30,10 +30,8 @@ pub struct Informant {
depth: usize,
pc: usize,
instruction: u8,
name: &'static str,
gas_cost: U256,
gas_used: U256,
stack_pop: usize,
stack: Vec<U256>,
memory: Vec<u8>,
storage: HashMap<H256, H256>,
@ -58,11 +56,19 @@ impl Informant {
}
impl vm::Informant for Informant {
fn before_test(&self, name: &str, action: &str) {
println!(
"{{\"test\":\"{name}\",\"action\":\"{action}\"}}",
name = name,
action = action,
);
}
fn set_gas(&mut self, gas: U256) {
self.gas_used = gas;
}
fn finish(&mut self, result: Result<vm::Success, vm::Failure>) {
fn finish(result: Result<vm::Success, vm::Failure>) {
match result {
Ok(success) => println!(
"{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}",
@ -112,7 +118,7 @@ impl trace::VMTracer for Informant {
self.gas_used = gas_used;
let len = self.stack.len();
self.stack.truncate(len - info.args);
self.stack.truncate(if len > info.args { len - info.args } else { 0 });
self.stack.extend_from_slice(stack_push);
if let Some((pos, data)) = mem_diff {

View File

@ -27,7 +27,11 @@ use info as vm;
pub struct Informant;
impl vm::Informant for Informant {
fn finish(&mut self, result: Result<vm::Success, vm::Failure>) {
fn before_test(&self, name: &str, action: &str) {
println!("Test: {} ({})", name, action);
}
fn finish(result: Result<vm::Success, vm::Failure>) {
match result {
Ok(success) => {
println!("Output: 0x{}", success.output.to_hex());

View File

@ -17,17 +17,19 @@
//! VM runner.
use std::time::{Instant, Duration};
use util::U256;
use ethcore::{trace, spec};
use ethcore::client::{EvmTestClient, EvmTestError};
use vm::ActionParams;
use util::{U256, H256};
use ethcore::{trace, spec, transaction, pod_state};
use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult};
use ethjson;
/// VM execution informant
pub trait Informant: trace::VMTracer {
/// Display a single run init message
fn before_test(&self, test: &str, action: &str);
/// Set initial gas.
fn set_gas(&mut self, _gas: U256) {}
/// Display final result.
fn finish(&mut self, result: Result<Success, Failure>);
fn finish(result: Result<Success, Failure>);
}
/// Execution finished correctly
@ -50,17 +52,71 @@ pub struct Failure {
pub time: Duration,
}
/// Execute given Transaction and verify resulting state root.
pub fn run_transaction<T: Informant>(
name: &str,
idx: usize,
spec: &ethjson::state::test::ForkSpec,
pre_state: &pod_state::PodState,
post_root: H256,
env_info: &client::EnvInfo,
transaction: transaction::SignedTransaction,
mut informant: T,
) {
let spec_name = format!("{:?}", spec).to_lowercase();
let spec = match EvmTestClient::spec_from_json(spec) {
Some(spec) => {
informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "starting");
spec
},
None => {
informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "skipping because of missing spec");
return;
},
};
informant.set_gas(env_info.gas_limit);
let result = run(spec, env_info.gas_limit, pre_state, |mut client| {
let result = client.transact(env_info, transaction, informant);
match result {
TransactResult::Ok { state_root, .. } if state_root != post_root => {
Err(EvmTestError::PostCondition(format!(
"State root mismatch (got: {}, expected: {})",
state_root,
post_root,
)))
},
TransactResult::Ok { gas_left, output, .. } => {
Ok((gas_left, output))
},
TransactResult::Err { error, .. } => {
Err(EvmTestError::PostCondition(format!(
"Unexpected execution error: {:?}", error
)))
},
}
});
T::finish(result)
}
/// Execute VM with given `ActionParams`
pub fn run<T: trace::VMTracer>(vm_tracer: &mut T, spec: spec::Spec, params: ActionParams) -> Result<Success, Failure> {
let mut test_client = EvmTestClient::new(spec).map_err(|error| Failure {
pub fn run<'a, F, T>(spec: &'a spec::Spec, initial_gas: U256, pre_state: T, run: F) -> Result<Success, Failure> where
F: FnOnce(EvmTestClient) -> Result<(U256, Vec<u8>), EvmTestError>,
T: Into<Option<&'a pod_state::PodState>>,
{
let test_client = match pre_state.into() {
Some(pre_state) => EvmTestClient::from_pod_state(spec, pre_state.clone()),
None => EvmTestClient::new(spec),
}.map_err(|error| Failure {
gas_used: 0.into(),
error,
time: Duration::from_secs(0)
})?;
let initial_gas = params.gas;
let start = Instant::now();
let result = test_client.call(params, vm_tracer);
let result = run(test_client);
let duration = start.elapsed();
match result {

View File

@ -17,8 +17,9 @@
//! Parity EVM interpreter binary.
#![warn(missing_docs)]
#![allow(dead_code)]
extern crate ethcore;
extern crate ethjson;
extern crate rustc_hex;
extern crate serde;
#[macro_use]
@ -31,6 +32,7 @@ extern crate panic_hook;
use std::sync::Arc;
use std::{fmt, fs};
use std::path::PathBuf;
use docopt::Docopt;
use rustc_hex::FromHex;
use util::{U256, Bytes, Address};
@ -47,6 +49,7 @@ EVM implementation for Parity.
Copyright 2016, 2017 Parity Technologies (UK) Ltd
Usage:
parity-evm state-test <file> [--json --only NAME --chain CHAIN]
parity-evm stats [options]
parity-evm [options]
parity-evm [-h | --help]
@ -59,6 +62,10 @@ Transaction options:
--gas GAS Supplied gas as hex (without 0x).
--gas-price WEI Supplied gas price as hex (without 0x).
State test options:
--only NAME Runs only a single test matching the name.
--chain CHAIN Run only tests from specific chain.
General options:
--json Display verbose results in JSON.
--chain CHAIN Chain spec file path.
@ -71,20 +78,67 @@ fn main() {
let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());
if args.flag_json {
run(args, display::json::Informant::default())
if args.cmd_state_test {
run_state_test(args)
} else if args.flag_json {
run_call(args, display::json::Informant::default())
} else {
run(args, display::simple::Informant::default())
run_call(args, display::simple::Informant::default())
}
}
fn run<T: Informant>(args: Args, mut informant: T) {
fn run_state_test(args: Args) {
use ethjson::state::test::Test;
let file = args.arg_file.expect("FILE is required");
let mut file = match fs::File::open(&file) {
Err(err) => die(format!("Unable to open: {:?}: {}", file, err)),
Ok(file) => file,
};
let state_test = match Test::load(&mut file) {
Err(err) => die(format!("Unable to load the test file: {}", err)),
Ok(test) => test,
};
let only_test = args.flag_only.map(|s| s.to_lowercase());
let only_chain = args.flag_chain.map(|s| s.to_lowercase());
for (name, test) in state_test {
if let Some(false) = only_test.as_ref().map(|only_test| &name.to_lowercase() == only_test) {
continue;
}
let multitransaction = test.transaction;
let env_info = test.env.into();
let pre = test.pre_state.into();
for (spec, states) in test.post_states {
if let Some(false) = only_chain.as_ref().map(|only_chain| &format!("{:?}", spec).to_lowercase() == only_chain) {
continue;
}
for (idx, state) in states.into_iter().enumerate() {
let post_root = state.hash.into();
let transaction = multitransaction.select(&state.indexes).into();
if args.flag_json {
let i = display::json::Informant::default();
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i)
} else {
let i = display::simple::Informant::default();
info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, i)
}
}
}
}
}
fn run_call<T: Informant>(args: Args, mut informant: T) {
let from = arg(args.from(), "--from");
let to = arg(args.to(), "--to");
let code = arg(args.code(), "--code");
let spec = arg(args.spec(), "--chain");
let gas = arg(args.gas(), "--gas");
let gas_price = arg(args.gas(), "--gas-price");
let gas_price = arg(args.gas_price(), "--gas-price");
let data = arg(args.data(), "--input");
if code.is_none() && to == Address::default() {
@ -103,13 +157,18 @@ fn run<T: Informant>(args: Args, mut informant: T) {
params.data = data;
informant.set_gas(gas);
let result = info::run(&mut informant, spec, params);
informant.finish(result);
let result = info::run(&spec, gas, None, |mut client| {
client.call(params, &mut informant)
});
T::finish(result);
}
#[derive(Debug, Deserialize)]
struct Args {
cmd_stats: bool,
cmd_state_test: bool,
arg_file: Option<PathBuf>,
flag_only: Option<String>,
flag_from: Option<String>,
flag_to: Option<String>,
flag_code: Option<String>,
@ -221,4 +280,22 @@ mod tests {
assert_eq!(args.data(), Ok(Some(vec![06])));
assert_eq!(args.flag_chain, Some("./testfile".to_owned()));
}
#[test]
fn should_parse_state_test_command() {
let args = run(&[
"parity-evm",
"state-test",
"./file.json",
"--chain", "homestead",
"--only=add11",
"--json",
]);
assert_eq!(args.cmd_state_test, true);
assert!(args.arg_file.is_some());
assert_eq!(args.flag_json, true);
assert_eq!(args.flag_chain, Some("homestead".to_owned()));
assert_eq!(args.flag_only, Some("add11".to_owned()));
}
}

View File

@ -385,6 +385,11 @@ export default class Parity {
.then(outNumber);
}
nodeHealth () {
return this._transport
.execute('parity_nodeHealth');
}
nodeName () {
return this._transport
.execute('parity_nodeName');

View File

@ -200,7 +200,7 @@ export default class Status {
const statusPromises = [
this._api.eth.syncing(),
this._api.parity.netPeers(),
this._fetchHealth()
this._api.parity.nodeHealth()
];
return Promise
@ -247,13 +247,6 @@ export default class Status {
};
}
_fetchHealth = () => {
// Support Parity-Extension.
const uiUrl = this._api.transport.uiUrlWithProtocol || '';
return fetch(`${uiUrl}/api/health`).then(res => res.json());
}
/**
* The data fetched here should not change
* unless Parity is restarted. They are thus

View File

@ -104,7 +104,10 @@ pub enum ForkSpec {
EIP158,
Frontier,
Homestead,
// TODO [ToDr] Deprecated
Metropolis,
Byzantium,
Constantinople,
}
/// State test indexes deserialization.

View File

@ -346,6 +346,7 @@ impl Configuration {
daemon: daemon,
logger_config: logger_config.clone(),
miner_options: self.miner_options(self.args.flag_reseal_min_period)?,
ntp_servers: self.ntp_servers(),
ws_conf: ws_conf,
http_conf: http_conf,
ipc_conf: ipc_conf,
@ -563,7 +564,6 @@ impl Configuration {
fn ui_config(&self) -> UiConfiguration {
UiConfiguration {
enabled: self.ui_enabled(),
ntp_servers: self.ntp_servers(),
interface: self.ui_interface(),
port: self.ui_port(),
hosts: self.ui_hosts(),
@ -576,7 +576,6 @@ impl Configuration {
DappsConfiguration {
enabled: self.dapps_enabled(),
ntp_servers: self.ntp_servers(),
dapps_path: PathBuf::from(self.directories().dapps),
extra_dapps: if self.args.cmd_dapp {
self.args.arg_path.iter().map(|path| PathBuf::from(path)).collect()
@ -1318,12 +1317,6 @@ mod tests {
support_token_api: true
}, UiConfiguration {
enabled: true,
ntp_servers: vec![
"0.parity.pool.ntp.org:123".into(),
"1.parity.pool.ntp.org:123".into(),
"2.parity.pool.ntp.org:123".into(),
"3.parity.pool.ntp.org:123".into(),
],
interface: "127.0.0.1".into(),
port: 8180,
hosts: Some(vec![]),
@ -1348,6 +1341,12 @@ mod tests {
daemon: None,
logger_config: Default::default(),
miner_options: Default::default(),
ntp_servers: vec![
"0.parity.pool.ntp.org:123".into(),
"1.parity.pool.ntp.org:123".into(),
"2.parity.pool.ntp.org:123".into(),
"3.parity.pool.ntp.org:123".into(),
],
ws_conf: Default::default(),
http_conf: Default::default(),
ipc_conf: Default::default(),
@ -1567,16 +1566,9 @@ mod tests {
let conf3 = parse(&["parity", "--ui-path", "signer", "--ui-interface", "test"]);
// then
let ntp_servers = vec![
"0.parity.pool.ntp.org:123".into(),
"1.parity.pool.ntp.org:123".into(),
"2.parity.pool.ntp.org:123".into(),
"3.parity.pool.ntp.org:123".into(),
];
assert_eq!(conf0.directories().signer, "signer".to_owned());
assert_eq!(conf0.ui_config(), UiConfiguration {
enabled: true,
ntp_servers: ntp_servers.clone(),
interface: "127.0.0.1".into(),
port: 8180,
hosts: Some(vec![]),
@ -1585,7 +1577,6 @@ mod tests {
assert_eq!(conf1.directories().signer, "signer".to_owned());
assert_eq!(conf1.ui_config(), UiConfiguration {
enabled: true,
ntp_servers: ntp_servers.clone(),
interface: "127.0.0.1".into(),
port: 8180,
hosts: Some(vec![]),
@ -1595,7 +1586,6 @@ mod tests {
assert_eq!(conf2.directories().signer, "signer".to_owned());
assert_eq!(conf2.ui_config(), UiConfiguration {
enabled: true,
ntp_servers: ntp_servers.clone(),
interface: "127.0.0.1".into(),
port: 3123,
hosts: Some(vec![]),
@ -1604,7 +1594,6 @@ mod tests {
assert_eq!(conf3.directories().signer, "signer".to_owned());
assert_eq!(conf3.ui_config(), UiConfiguration {
enabled: true,
ntp_servers: ntp_servers.clone(),
interface: "test".into(),
port: 8180,
hosts: Some(vec![]),

View File

@ -22,12 +22,12 @@ use ethcore::client::{Client, BlockChainClient, BlockId};
use ethcore::transaction::{Transaction, Action};
use ethsync::LightSync;
use futures::{future, IntoFuture, Future, BoxFuture};
use futures_cpupool::CpuPool;
use hash_fetch::fetch::Client as FetchClient;
use hash_fetch::urlhint::ContractClient;
use helpers::replace_home;
use light::client::Client as LightClient;
use light::on_demand::{self, OnDemand};
use node_health::{SyncStatus, NodeHealth};
use rpc;
use rpc_apis::SignerService;
use parity_reactor;
@ -36,7 +36,6 @@ use util::{Bytes, Address};
#[derive(Debug, PartialEq, Clone)]
pub struct Configuration {
pub enabled: bool,
pub ntp_servers: Vec<String>,
pub dapps_path: PathBuf,
pub extra_dapps: Vec<PathBuf>,
pub extra_embed_on: Vec<(String, u16)>,
@ -48,12 +47,6 @@ impl Default for Configuration {
let data_dir = default_data_path();
Configuration {
enabled: true,
ntp_servers: vec![
"0.parity.pool.ntp.org:123".into(),
"1.parity.pool.ntp.org:123".into(),
"2.parity.pool.ntp.org:123".into(),
"3.parity.pool.ntp.org:123".into(),
],
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
extra_dapps: vec![],
extra_embed_on: vec![],
@ -149,10 +142,10 @@ impl ContractClient for LightRegistrar {
// to resolve.
#[derive(Clone)]
pub struct Dependencies {
pub node_health: NodeHealth,
pub sync_status: Arc<SyncStatus>,
pub contract_client: Arc<ContractClient>,
pub remote: parity_reactor::TokioRemote,
pub pool: CpuPool,
pub fetch: FetchClient,
pub signer: Arc<SignerService>,
pub ui_address: Option<(String, u16)>,
@ -165,7 +158,6 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Mi
server::dapps_middleware(
deps,
&configuration.ntp_servers,
configuration.dapps_path,
configuration.extra_dapps,
rpc::DAPPS_DOMAIN,
@ -174,19 +166,18 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Mi
).map(Some)
}
pub fn new_ui(enabled: bool, ntp_servers: &[String], deps: Dependencies) -> Result<Option<Middleware>, String> {
pub fn new_ui(enabled: bool, deps: Dependencies) -> Result<Option<Middleware>, String> {
if !enabled {
return Ok(None);
}
server::ui_middleware(
deps,
ntp_servers,
rpc::DAPPS_DOMAIN,
).map(Some)
}
pub use self::server::{SyncStatus, Middleware, service};
pub use self::server::{Middleware, service};
#[cfg(not(feature = "dapps"))]
mod server {
@ -196,11 +187,6 @@ mod server {
use parity_rpc::{hyper, RequestMiddleware, RequestMiddlewareAction};
use rpc_apis;
pub trait SyncStatus {
fn is_major_importing(&self) -> bool;
fn peers(&self) -> (usize, usize);
}
pub struct Middleware;
impl RequestMiddleware for Middleware {
fn on_request(
@ -212,7 +198,6 @@ mod server {
pub fn dapps_middleware(
_deps: Dependencies,
_ntp_servers: &[String],
_dapps_path: PathBuf,
_extra_dapps: Vec<PathBuf>,
_dapps_domain: &str,
@ -224,7 +209,6 @@ mod server {
pub fn ui_middleware(
_deps: Dependencies,
_ntp_servers: &[String],
_dapps_domain: &str,
) -> Result<Middleware, String> {
Err("Your Parity version has been compiled without UI support.".into())
@ -246,11 +230,9 @@ mod server {
use parity_reactor;
pub use parity_dapps::Middleware;
pub use parity_dapps::SyncStatus;
pub fn dapps_middleware(
deps: Dependencies,
ntp_servers: &[String],
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
dapps_domain: &str,
@ -262,8 +244,7 @@ mod server {
let web_proxy_tokens = Arc::new(move |token| signer.web_proxy_access_token_domain(&token));
Ok(parity_dapps::Middleware::dapps(
ntp_servers,
deps.pool,
deps.node_health,
parity_remote,
deps.ui_address,
extra_embed_on,
@ -280,13 +261,11 @@ mod server {
pub fn ui_middleware(
deps: Dependencies,
ntp_servers: &[String],
dapps_domain: &str,
) -> Result<Middleware, String> {
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
Ok(parity_dapps::Middleware::ui(
ntp_servers,
deps.pool,
deps.node_health,
parity_remote,
dapps_domain,
deps.contract_client,

View File

@ -58,6 +58,7 @@ extern crate ethcore_util as util;
extern crate ethcore_network as network;
extern crate ethkey;
extern crate ethsync;
extern crate node_health;
extern crate panic_hook;
extern crate parity_hash_fetch as hash_fetch;
extern crate parity_ipfs_api;

View File

@ -73,7 +73,6 @@ impl Default for HttpConfiguration {
#[derive(Debug, PartialEq, Clone)]
pub struct UiConfiguration {
pub enabled: bool,
pub ntp_servers: Vec<String>,
pub interface: String,
pub port: u16,
pub hosts: Option<Vec<String>>,
@ -107,12 +106,6 @@ impl Default for UiConfiguration {
fn default() -> Self {
UiConfiguration {
enabled: true && cfg!(feature = "ui-enabled"),
ntp_servers: vec![
"0.parity.pool.ntp.org:123".into(),
"1.parity.pool.ntp.org:123".into(),
"2.parity.pool.ntp.org:123".into(),
"3.parity.pool.ntp.org:123".into(),
],
port: 8180,
interface: "127.0.0.1".into(),
hosts: Some(vec![]),

View File

@ -27,17 +27,18 @@ use ethcore::account_provider::AccountProvider;
use ethcore::client::Client;
use ethcore::miner::{Miner, ExternalMiner};
use ethcore::snapshot::SnapshotService;
use parity_rpc::{Metadata, NetworkSettings};
use parity_rpc::informant::{ActivityNotifier, ClientNotifier};
use parity_rpc::dispatch::{FullDispatcher, LightDispatcher};
use ethcore_logger::RotatingLogger;
use ethsync::{ManageNetwork, SyncProvider, LightSync};
use hash_fetch::fetch::Client as FetchClient;
use jsonrpc_core::{self as core, MetaIoHandler};
use light::{TransactionQueue as LightTransactionQueue, Cache as LightDataCache};
use node_health::NodeHealth;
use parity_reactor;
use parity_rpc::dispatch::{FullDispatcher, LightDispatcher};
use parity_rpc::informant::{ActivityNotifier, ClientNotifier};
use parity_rpc::{Metadata, NetworkSettings};
use updater::Updater;
use util::{Mutex, RwLock};
use ethcore_logger::RotatingLogger;
use parity_reactor;
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub enum Api {
@ -217,6 +218,7 @@ pub struct FullDependencies {
pub settings: Arc<NetworkSettings>,
pub net_service: Arc<ManageNetwork>,
pub updater: Arc<Updater>,
pub health: NodeHealth,
pub geth_compatibility: bool,
pub dapps_service: Option<Arc<DappsService>>,
pub dapps_address: Option<(String, u16)>,
@ -300,12 +302,13 @@ impl FullDependencies {
false => None,
};
handler.extend_with(ParityClient::new(
&self.client,
&self.miner,
&self.sync,
&self.updater,
&self.net_service,
&self.secret_store,
self.client.clone(),
self.miner.clone(),
self.sync.clone(),
self.updater.clone(),
self.net_service.clone(),
self.health.clone(),
self.secret_store.clone(),
self.logger.clone(),
self.settings.clone(),
signer,
@ -403,6 +406,7 @@ pub struct LightDependencies {
pub secret_store: Arc<AccountProvider>,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
pub health: NodeHealth,
pub on_demand: Arc<::light::on_demand::OnDemand>,
pub cache: Arc<Mutex<LightDataCache>>,
pub transaction_queue: Arc<RwLock<LightTransactionQueue>>,
@ -507,6 +511,7 @@ impl LightDependencies {
self.secret_store.clone(),
self.logger.clone(),
self.settings.clone(),
self.health.clone(),
signer,
self.dapps_address.clone(),
self.ws_address.clone(),

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use std::sync::Arc;
use std::net::{TcpListener};
@ -32,6 +33,7 @@ use fdlimit::raise_fd_limit;
use hash_fetch::fetch::{Fetch, Client as FetchClient};
use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
use light::Cache as LightDataCache;
use node_health;
use parity_reactor::EventLoop;
use parity_rpc::{NetworkSettings, informant, is_major_importing};
use updater::{UpdatePolicy, Updater};
@ -80,6 +82,7 @@ pub struct RunCmd {
pub daemon: Option<String>,
pub logger_config: LogConfig,
pub miner_options: MinerOptions,
pub ntp_servers: Vec<String>,
pub ws_conf: rpc::WsConfiguration,
pub http_conf: rpc::HttpConfiguration,
pub ipc_conf: rpc::IpcConfiguration,
@ -283,7 +286,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
// the dapps server
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.ui_conf, &cmd.logger_config));
let dapps_deps = {
let (node_health, dapps_deps) = {
let contract_client = Arc::new(::dapps::LightRegistrar {
client: service.client().clone(),
sync: light_sync.clone(),
@ -291,7 +294,12 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
});
struct LightSyncStatus(Arc<LightSync>);
impl dapps::SyncStatus for LightSyncStatus {
impl fmt::Debug for LightSyncStatus {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Light Sync Status")
}
}
impl node_health::SyncStatus for LightSyncStatus {
fn is_major_importing(&self) -> bool { self.0.is_major_importing() }
fn peers(&self) -> (usize, usize) {
let peers = ethsync::LightSyncProvider::peer_numbers(&*self.0);
@ -299,19 +307,26 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
}
}
dapps::Dependencies {
sync_status: Arc::new(LightSyncStatus(light_sync.clone())),
pool: fetch.pool(),
let sync_status = Arc::new(LightSyncStatus(light_sync.clone()));
let node_health = node_health::NodeHealth::new(
sync_status.clone(),
node_health::TimeChecker::new(&cmd.ntp_servers, fetch.pool()),
event_loop.remote(),
);
(node_health.clone(), dapps::Dependencies {
sync_status,
node_health,
contract_client: contract_client,
remote: event_loop.raw_remote(),
fetch: fetch.clone(),
signer: signer_service.clone(),
ui_address: cmd.ui_conf.address(),
}
})
};
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, &cmd.ui_conf.ntp_servers, dapps_deps)?;
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, dapps_deps)?;
// start RPCs
let dapps_service = dapps::service(&dapps_middleware);
@ -320,6 +335,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
client: service.client().clone(),
sync: light_sync.clone(),
net: light_sync.clone(),
health: node_health,
secret_store: account_provider,
logger: logger,
settings: Arc::new(cmd.net_settings),
@ -661,12 +677,17 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.ui_conf, &cmd.logger_config));
// the dapps server
let dapps_deps = {
let (node_health, dapps_deps) = {
let (sync, client) = (sync_provider.clone(), client.clone());
let contract_client = Arc::new(::dapps::FullRegistrar { client: client.clone() });
struct SyncStatus(Arc<ethsync::SyncProvider>, Arc<Client>, ethsync::NetworkConfiguration);
impl dapps::SyncStatus for SyncStatus {
impl fmt::Debug for SyncStatus {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Dapps Sync Status")
}
}
impl node_health::SyncStatus for SyncStatus {
fn is_major_importing(&self) -> bool {
is_major_importing(Some(self.0.status().state), self.1.queue_info())
}
@ -676,18 +697,24 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
}
}
dapps::Dependencies {
sync_status: Arc::new(SyncStatus(sync, client, net_conf)),
pool: fetch.pool(),
let sync_status = Arc::new(SyncStatus(sync, client, net_conf));
let node_health = node_health::NodeHealth::new(
sync_status.clone(),
node_health::TimeChecker::new(&cmd.ntp_servers, fetch.pool()),
event_loop.remote(),
);
(node_health.clone(), dapps::Dependencies {
sync_status,
node_health,
contract_client: contract_client,
remote: event_loop.raw_remote(),
fetch: fetch.clone(),
signer: signer_service.clone(),
ui_address: cmd.ui_conf.address(),
}
})
};
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, &cmd.ui_conf.ntp_servers, dapps_deps)?;
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, dapps_deps)?;
let dapps_service = dapps::service(&dapps_middleware);
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
@ -695,6 +722,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
snapshot: snapshot_service.clone(),
client: client.clone(),
sync: sync_provider.clone(),
health: node_health,
net: manage_network.clone(),
secret_store: secret_store,
miner: miner.clone(),

View File

@ -47,12 +47,13 @@ ethjson = { path = "../json" }
ethcore-devtools = { path = "../devtools" }
ethcore-light = { path = "../ethcore/light" }
ethcore-logger = { path = "../logger" }
vm = { path = "../ethcore/vm" }
parity-updater = { path = "../updater" }
parity-reactor = { path = "../util/reactor" }
rlp = { path = "../util/rlp" }
fetch = { path = "../util/fetch" }
node-health = { path = "../dapps/node-health" }
parity-reactor = { path = "../util/reactor" }
parity-updater = { path = "../updater" }
rlp = { path = "../util/rlp" }
stats = { path = "../util/stats" }
vm = { path = "../ethcore/vm" }
clippy = { version = "0.0.103", optional = true}
pretty_assertions = "0.1"

View File

@ -55,6 +55,7 @@ extern crate ethsync;
extern crate ethcore_logger;
extern crate vm;
extern crate fetch;
extern crate node_health;
extern crate parity_reactor;
extern crate parity_updater as updater;
extern crate rlp;

View File

@ -584,8 +584,9 @@ fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: S
}
/// Extract the default gas price from a client and miner.
pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256
where C: MiningBlockChainClient, M: MinerService
pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where
C: MiningBlockChainClient,
M: MinerService,
{
client.gas_price_corpus(100).median().cloned().unwrap_or_else(|| miner.sensible_gas_price())
}

View File

@ -24,8 +24,8 @@ use jsonrpc_core::Error;
use v1::helpers::CallRequest;
use v1::helpers::dispatch::default_gas_price;
pub fn sign_call<B: MiningBlockChainClient, M: MinerService>(
client: &Arc<B>,
pub fn sign_call<C: MiningBlockChainClient, M: MinerService> (
client: &Arc<C>,
miner: &Arc<M>,
request: CallRequest,
gas_cap: bool,

View File

@ -19,15 +19,15 @@ use std::sync::Arc;
use std::collections::{BTreeMap, HashSet};
use futures::{future, Future, BoxFuture};
use ethcore_logger::RotatingLogger;
use util::misc::version_data;
use crypto::ecies;
use crypto::{ecies, DEFAULT_MAC};
use ethkey::{Brain, Generator};
use ethstore::random_phrase;
use ethsync::LightSyncProvider;
use ethcore::account_provider::AccountProvider;
use crypto::DEFAULT_MAC;
use ethcore_logger::RotatingLogger;
use node_health::{NodeHealth, Health};
use light::client::LightChainClient;
@ -53,6 +53,7 @@ pub struct ParityClient {
accounts: Arc<AccountProvider>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
health: NodeHealth,
signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>,
@ -67,18 +68,20 @@ impl ParityClient {
accounts: Arc<AccountProvider>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
health: NodeHealth,
signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>,
) -> Self {
ParityClient {
light_dispatch: light_dispatch,
accounts: accounts,
logger: logger,
settings: settings,
signer: signer,
dapps_address: dapps_address,
ws_address: ws_address,
light_dispatch,
accounts,
logger,
settings,
health,
signer,
dapps_address,
ws_address,
eip86_transition: client.eip86_transition(),
}
}
@ -393,4 +396,10 @@ impl Parity for ParityClient {
fn call(&self, _meta: Self::Metadata, _requests: Vec<CallRequest>, _block: Trailing<BlockNumber>) -> BoxFuture<Vec<Bytes>, Error> {
future::err(errors::light_unimplemented(None)).boxed()
}
fn node_health(&self) -> BoxFuture<Health, Error> {
self.health.health()
.map_err(|err| errors::internal("Health API failure.", err))
.boxed()
}
}

View File

@ -20,11 +20,10 @@ use std::str::FromStr;
use std::collections::{BTreeMap, HashSet};
use futures::{future, Future, BoxFuture};
use ethcore_logger::RotatingLogger;
use util::Address;
use util::misc::version_data;
use crypto::ecies;
use crypto::{DEFAULT_MAC, ecies};
use ethkey::{Brain, Generator};
use ethstore::random_phrase;
use ethsync::{SyncProvider, ManageNetwork};
@ -34,8 +33,9 @@ use ethcore::ids::BlockId;
use ethcore::miner::MinerService;
use ethcore::mode::Mode;
use ethcore::transaction::SignedTransaction;
use ethcore_logger::RotatingLogger;
use node_health::{NodeHealth, Health};
use updater::{Service as UpdateService};
use crypto::DEFAULT_MAC;
use jsonrpc_core::Error;
use jsonrpc_macros::Trailing;
@ -53,17 +53,13 @@ use v1::types::{
};
/// Parity implementation.
pub struct ParityClient<C, M, S: ?Sized, U> where
C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider,
U: UpdateService,
{
pub struct ParityClient<C, M, U> {
client: Arc<C>,
miner: Arc<M>,
sync: Arc<S>,
updater: Arc<U>,
sync: Arc<SyncProvider>,
net: Arc<ManageNetwork>,
health: NodeHealth,
accounts: Option<Arc<AccountProvider>>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
@ -73,39 +69,39 @@ pub struct ParityClient<C, M, S: ?Sized, U> where
eip86_transition: u64,
}
impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
impl<C, M, U> ParityClient<C, M, U> where
C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider,
U: UpdateService,
{
/// Creates new `ParityClient`.
pub fn new(
client: &Arc<C>,
miner: &Arc<M>,
sync: &Arc<S>,
updater: &Arc<U>,
net: &Arc<ManageNetwork>,
store: &Option<Arc<AccountProvider>>,
client: Arc<C>,
miner: Arc<M>,
sync: Arc<SyncProvider>,
updater: Arc<U>,
net: Arc<ManageNetwork>,
health: NodeHealth,
accounts: Option<Arc<AccountProvider>>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>,
) -> Self {
let eip86_transition = client.eip86_transition();
ParityClient {
client: client.clone(),
miner: miner.clone(),
sync: sync.clone(),
updater: updater.clone(),
net: net.clone(),
accounts: store.clone(),
logger: logger,
settings: settings,
signer: signer,
dapps_address: dapps_address,
ws_address: ws_address,
eip86_transition: client.eip86_transition(),
client,
miner,
sync,
updater,
net,
health,
accounts,
logger,
settings,
signer,
dapps_address,
ws_address,
eip86_transition,
}
}
@ -116,10 +112,9 @@ impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
}
}
impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
M: MinerService + 'static,
impl<C, M, U> Parity for ParityClient<C, M, U> where
C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static,
M: MinerService + 'static,
U: UpdateService + 'static,
{
type Metadata = Metadata;
@ -429,4 +424,10 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
future::done(result).boxed()
}
fn node_health(&self) -> BoxFuture<Health, Error> {
self.health.health()
.map_err(|err| errors::internal("Health API failure.", err))
.boxed()
}
}

View File

@ -15,13 +15,15 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use ethcore_logger::RotatingLogger;
use util::{Address, U256};
use ethsync::ManageNetwork;
use ethcore::account_provider::AccountProvider;
use ethcore::client::{TestBlockChainClient, Executed};
use ethcore::miner::LocalTransactionStatus;
use ethcore_logger::RotatingLogger;
use ethstore::ethkey::{Generator, Random};
use ethsync::ManageNetwork;
use node_health::{self, NodeHealth};
use parity_reactor;
use util::Address;
use jsonrpc_core::IoHandler;
use v1::{Parity, ParityClient};
@ -30,13 +32,14 @@ use v1::helpers::{SignerService, NetworkSettings};
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestUpdater};
use super::manage_network::TestManageNetwork;
pub type TestParityClient = ParityClient<TestBlockChainClient, TestMinerService, TestSyncProvider, TestUpdater>;
pub type TestParityClient = ParityClient<TestBlockChainClient, TestMinerService, TestUpdater>;
pub struct Dependencies {
pub miner: Arc<TestMinerService>,
pub client: Arc<TestBlockChainClient>,
pub sync: Arc<TestSyncProvider>,
pub updater: Arc<TestUpdater>,
pub health: NodeHealth,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
pub network: Arc<ManageNetwork>,
@ -54,6 +57,11 @@ impl Dependencies {
network_id: 3,
num_peers: 120,
})),
health: NodeHealth::new(
Arc::new(FakeSync),
node_health::TimeChecker::new::<String>(&[], node_health::CpuPool::new(1)),
parity_reactor::Remote::new_sync(),
),
updater: Arc::new(TestUpdater::default()),
logger: Arc::new(RotatingLogger::new("rpc=trace".to_owned())),
settings: Arc::new(NetworkSettings {
@ -75,12 +83,13 @@ impl Dependencies {
let opt_accounts = Some(self.accounts.clone());
ParityClient::new(
&self.client,
&self.miner,
&self.sync,
&self.updater,
&self.network,
&opt_accounts,
self.client.clone(),
self.miner.clone(),
self.sync.clone(),
self.updater.clone(),
self.network.clone(),
self.health.clone(),
opt_accounts.clone(),
self.logger.clone(),
self.settings.clone(),
signer,
@ -102,6 +111,13 @@ impl Dependencies {
}
}
#[derive(Debug)]
struct FakeSync;
impl node_health::SyncStatus for FakeSync {
fn is_major_importing(&self) -> bool { false }
fn peers(&self) -> (usize, usize) { (4, 25) }
}
#[test]
fn rpc_parity_accounts_info() {
let deps = Dependencies::new();
@ -507,6 +523,8 @@ fn rpc_parity_cid() {
#[test]
fn rpc_parity_call() {
use util::U256;
let deps = Dependencies::new();
deps.client.set_execution_result(Ok(Executed {
exception: None,
@ -541,3 +559,14 @@ fn rpc_parity_call() {
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_node_health() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_nodeHealth", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"peers":{"details":[4,25],"message":"","status":"ok"},"sync":{"details":false,"message":"","status":"ok"},"time":{"details":0,"message":"","status":"ok"}},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}

View File

@ -22,6 +22,7 @@ use jsonrpc_core::Error;
use jsonrpc_macros::Trailing;
use futures::BoxFuture;
use node_health::Health;
use v1::types::{
H160, H256, H512, U256, Bytes, CallRequest,
Peers, Transaction, RpcSettings, Histogram,
@ -207,5 +208,9 @@ build_rpc_trait! {
/// Call contract, returning the output data.
#[rpc(meta, name = "parity_call")]
fn call(&self, Self::Metadata, Vec<CallRequest>, Trailing<BlockNumber>) -> BoxFuture<Vec<Bytes>, Error>;
/// Returns node's health report.
#[rpc(async, name = "parity_nodeHealth")]
fn node_health(&self) -> BoxFuture<Health, Error>;
}
}

View File

@ -37,9 +37,9 @@ struct BitVecJournal {
impl BitVecJournal {
pub fn new(size: usize) -> BitVecJournal {
let extra = if size % 8 > 0 { 1 } else { 0 };
let extra = if size % 64 > 0 { 1 } else { 0 };
BitVecJournal {
elems: vec![0u64; size / 8 + extra],
elems: vec![0u64; size / 64 + extra],
journal: HashSet::new(),
}
}

View File

@ -20,7 +20,7 @@
extern crate futures;
extern crate tokio_core;
use std::thread;
use std::{fmt, thread};
use std::sync::mpsc;
use std::time::Duration;
use futures::{Future, IntoFuture};
@ -81,7 +81,19 @@ enum Mode {
ThreadPerFuture,
}
#[derive(Clone)]
impl fmt::Debug for Mode {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::Mode::*;
match *self {
Tokio(_) => write!(fmt, "tokio"),
Sync => write!(fmt, "synchronous"),
ThreadPerFuture => write!(fmt, "thread per future"),
}
}
}
#[derive(Debug, Clone)]
pub struct Remote {
inner: Mode,
}