Merge branch 'master' into light-poa

This commit is contained in:
Robert Habermeier 2017-08-29 15:09:21 +02:00
commit d3c3e0b164
91 changed files with 1969 additions and 783 deletions

45
Cargo.lock generated
View File

@ -548,6 +548,7 @@ dependencies = [
"bigint 4.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "bigint 4.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"plain_hasher 0.1.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -935,6 +936,7 @@ dependencies = [
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.8.0", "ethcore 1.8.0",
"ethcore-util 1.8.0", "ethcore-util 1.8.0",
"ethjson 0.1.0",
"evm 0.1.0", "evm 0.1.0",
"panic_hook 0.1.0", "panic_hook 0.1.0",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1613,6 +1615,34 @@ dependencies = [
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "node-filter"
version = "1.8.0"
dependencies = [
"ethcore 1.8.0",
"ethcore-io 1.8.0",
"ethcore-network 1.8.0",
"ethcore-util 1.8.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"native-contracts 0.1.0",
]
[[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]] [[package]]
name = "nodrop" name = "nodrop"
version = "0.1.9" version = "0.1.9"
@ -1821,6 +1851,8 @@ dependencies = [
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"node-filter 1.8.0",
"node-health 0.1.0",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"panic_hook 0.1.0", "panic_hook 0.1.0",
@ -1862,7 +1894,6 @@ dependencies = [
"ethcore-util 1.8.0", "ethcore-util 1.8.0",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1870,7 +1901,7 @@ dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "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 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)", "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-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-hash-fetch 1.8.0", "parity-hash-fetch 1.8.0",
"parity-reactor 0.1.0", "parity-reactor 0.1.0",
@ -1985,6 +2016,7 @@ dependencies = [
"jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "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)", "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)", "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)", "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0", "parity-reactor 0.1.0",
"parity-updater 1.8.0", "parity-updater 1.8.0",
@ -2058,7 +2090,7 @@ dependencies = [
[[package]] [[package]]
name = "parity-ui-precompiled" name = "parity-ui-precompiled"
version = "1.4.0" version = "1.4.0"
source = "git+https://github.com/paritytech/js-precompiled.git#416ced84c23b1a776d53ee4a3023eb4eb4736cf8" source = "git+https://github.com/paritytech/js-precompiled.git#d809723e58bcb36c0f8d2eca5ca94abbb3690544"
dependencies = [ dependencies = [
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -2197,6 +2229,13 @@ name = "pkg-config"
version = "0.3.9" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "plain_hasher"
version = "0.1.0"
dependencies = [
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "podio" name = "podio"
version = "0.1.5" version = "0.1.5"

View File

@ -42,7 +42,9 @@ ethcore-light = { path = "ethcore/light" }
ethcore-logger = { path = "logger" } ethcore-logger = { path = "logger" }
ethcore-stratum = { path = "stratum" } ethcore-stratum = { path = "stratum" }
ethcore-network = { path = "util/network" } ethcore-network = { path = "util/network" }
node-filter = { path = "ethcore/node_filter" }
ethkey = { path = "ethkey" } ethkey = { path = "ethkey" }
node-health = { path = "dapps/node-health" }
rlp = { path = "util/rlp" } rlp = { path = "util/rlp" }
rpc-cli = { path = "rpc_cli" } rpc-cli = { path = "rpc_cli" }
parity-hash-fetch = { path = "hash-fetch" } parity-hash-fetch = { path = "hash-fetch" }
@ -110,4 +112,4 @@ lto = false
panic = "abort" panic = "abort"
[workspace] [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] [dependencies]
base32 = "0.3" base32 = "0.3"
env_logger = "0.4"
futures = "0.1" futures = "0.1"
futures-cpupool = "0.1"
linked-hash-map = "0.3" linked-hash-map = "0.3"
log = "0.3" log = "0.3"
parity-dapps-glue = "1.7" parity-dapps-glue = "1.7"
mime = "0.2" mime = "0.2"
mime_guess = "1.6.1" mime_guess = "1.6.1"
ntp = "0.2.0"
rand = "0.3" rand = "0.3"
rustc-hex = "1.0" rustc-hex = "1.0"
serde = "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-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" } jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
ethcore-devtools = { path = "../devtools" }
ethcore-util = { path = "../util" } ethcore-util = { path = "../util" }
fetch = { path = "../util/fetch" } fetch = { path = "../util/fetch" }
node-health = { path = "./node-health" }
parity-hash-fetch = { path = "../hash-fetch" } parity-hash-fetch = { path = "../hash-fetch" }
parity-reactor = { path = "../util/reactor" } parity-reactor = { path = "../util/reactor" }
parity-ui = { path = "./ui" } parity-ui = { path = "./ui" }
clippy = { version = "0.0.103", optional = true} clippy = { version = "0.0.103", optional = true}
[dev-dependencies]
env_logger = "0.4"
ethcore-devtools = { path = "../devtools" }
[features] [features]
dev = ["clippy", "ethcore-util/dev"] 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::future::{self, IntoFuture};
use futures_cpupool::{CpuPool, CpuFuture}; use futures_cpupool::{CpuPool, CpuFuture};
use ntp; use ntp;
use time::{Duration, Timespec}; use parking_lot::RwLock;
use util::RwLock; use time_crate::{Duration, Timespec};
/// Time checker error. /// Time checker error.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -164,7 +164,7 @@ impl Ntp for SimpleNtp {
match ntp::request(&server.address) { match ntp::request(&server.address) {
Ok(packet) => { 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 orig_time = Timespec::from(packet.orig_time);
let recv_time = Timespec::from(packet.recv_time); let recv_time = Timespec::from(packet.recv_time);
let transmit_time = Timespec::from(packet.transmit_time); let transmit_time = Timespec::from(packet.transmit_time);
@ -293,7 +293,7 @@ mod tests {
use time::Duration; use time::Duration;
use futures::{future, Future}; use futures::{future, Future};
use super::{Ntp, TimeChecker, Error}; use super::{Ntp, TimeChecker, Error};
use util::RwLock; use parking_lot::RwLock;
#[derive(Clone)] #[derive(Clone)]
struct FakeNtp(RefCell<Vec<Duration>>, Cell<u64>); 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 hyper::status::StatusCode;
use api::{response, types}; use api::{response, types};
use api::time::{TimeChecker, MAX_DRIFT};
use apps::fetcher::Fetcher; use apps::fetcher::Fetcher;
use handlers::{self, extract_url}; use handlers::{self, extract_url};
use endpoint::{Endpoint, Handler, EndpointPath}; use endpoint::{Endpoint, Handler, EndpointPath};
use node_health::{NodeHealth, HealthStatus, Health};
use parity_reactor::Remote; use parity_reactor::Remote;
use {SyncStatus};
#[derive(Clone)] #[derive(Clone)]
pub struct RestApi { pub struct RestApi {
fetcher: Arc<Fetcher>, fetcher: Arc<Fetcher>,
sync_status: Arc<SyncStatus>, health: NodeHealth,
time: TimeChecker,
remote: Remote, remote: Remote,
} }
impl RestApi { impl RestApi {
pub fn new( pub fn new(
fetcher: Arc<Fetcher>, fetcher: Arc<Fetcher>,
sync_status: Arc<SyncStatus>, health: NodeHealth,
time: TimeChecker,
remote: Remote, remote: Remote,
) -> Box<Endpoint> { ) -> Box<Endpoint> {
Box::new(RestApi { Box::new(RestApi {
fetcher, fetcher,
sync_status, health,
time,
remote, remote,
}) })
} }
@ -90,68 +86,23 @@ impl RestApiRouter {
} }
fn health(&self, control: Control) -> Box<Handler> { fn health(&self, control: Control) -> Box<Handler> {
use self::types::{HealthInfo, HealthStatus, Health}; let map = move |health: Result<Result<Health, ()>, ()>| {
let status = match health {
trace!(target: "dapps", "Checking node health."); Ok(Ok(ref health)) => {
// Check timediff if [&health.peers.status, &health.sync.status].iter().any(|x| *x != &HealthStatus::Ok) {
let sync_status = self.api.sync_status.clone(); StatusCode::PreconditionFailed // HTTP 412
let map = move |time| { } else {
// Check peers StatusCode::Ok // HTTP 200
let peers = { }
let (connected, max) = sync_status.peers(); },
let (status, message) = match connected { _ => StatusCode::ServiceUnavailable, // HTTP 503
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 response::as_json(status, &health)
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 })
}; };
let health = self.api.health.health();
let time = self.api.time.time_drift();
let remote = self.api.remote.clone(); 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 api;
mod response; mod response;
mod time;
mod types; mod types;
pub use self::api::RestApi; pub use self::api::RestApi;
pub use self::time::TimeChecker;

View File

@ -25,43 +25,3 @@ pub struct ApiError {
/// More technical error details. /// More technical error details.
pub detail: String, 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); struct FakeSync(bool);
impl SyncStatus for FakeSync { impl SyncStatus for FakeSync {
fn is_major_importing(&self) -> bool { self.0 } fn is_major_importing(&self) -> bool { self.0 }

View File

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

View File

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

View File

@ -44,7 +44,7 @@ pub trait Memory {
} }
/// Checks whether offset and size is valid memory range /// Checks whether offset and size is valid memory range
fn is_valid_range(off: usize, size: usize) -> bool { pub fn is_valid_range(off: usize, size: usize) -> bool {
// When size is zero we haven't actually expanded the memory // When size is zero we haven't actually expanded the memory
let overflow = off.overflowing_add(size).1; let overflow = off.overflowing_add(size).1;
size > 0 && !overflow size > 0 && !overflow

View File

@ -168,7 +168,12 @@ impl<Cost: CostType> vm::Vm for Interpreter<Cost> {
} }
if do_trace { if do_trace {
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written); ext.trace_executed(
gasometer.current_gas.as_u256(),
stack.peek_top(info.ret),
mem_written.map(|(o, s)| (o, &(self.mem[o..o+s]))),
store_written,
);
} }
// Advance // Advance
@ -252,14 +257,20 @@ impl<Cost: CostType> Interpreter<Cost> {
instruction: Instruction, instruction: Instruction,
stack: &Stack<U256> stack: &Stack<U256>
) -> Option<(usize, usize)> { ) -> Option<(usize, usize)> {
match instruction { let read = |pos| stack.peek(pos).low_u64() as usize;
instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)), let written = match instruction {
instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)), instructions::MSTORE | instructions::MLOAD => Some((read(0), 32)),
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)), instructions::MSTORE8 => Some((read(0), 1)),
instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)), instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => Some((read(0), read(2))),
instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)), instructions::EXTCODECOPY => Some((read(1), read(3))),
instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)), instructions::CALL | instructions::CALLCODE => Some((read(5), read(6))),
instructions::DELEGATECALL | instructions::STATICCALL => Some((read(4), read(5))),
_ => None, _ => None,
};
match written {
Some((offset, size)) if !memory::is_valid_range(offset, size) => None,
written => written,
} }
} }
@ -862,3 +873,36 @@ fn address_to_u256(value: Address) -> U256 {
U256::from(&*H256::from(value)) U256::from(&*H256::from(value))
} }
#[cfg(test)]
mod tests {
use std::sync::Arc;
use rustc_hex::FromHex;
use vmtype::VMType;
use factory::Factory;
use vm::{self, ActionParams, ActionValue};
use vm::tests::{FakeExt, test_finalize};
#[test]
fn should_not_fail_on_tracing_mem() {
let code = "7feeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000620f120660406000601773945304eb96065b2a98b57a48a06ae28d285a71b56101f4f1600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = 5.into();
params.gas = 300_000.into();
params.gas_price = 1.into();
params.value = ActionValue::Transfer(100_000.into());
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new();
ext.balances.insert(5.into(), 1_000_000_000.into());
ext.tracing = true;
let gas_left = {
let mut vm = Factory::new(VMType::Interpreter, 1).create(params.gas);
test_finalize(vm.exec(params, &mut ext)).unwrap()
};
assert_eq!(ext.calls.len(), 1);
assert_eq!(gas_left, 248_212.into());
}
}

View File

@ -28,6 +28,7 @@ const SERVICE_TRANSACTION_ABI: &'static str = include_str!("res/service_transact
const SECRETSTORE_ACL_STORAGE_ABI: &'static str = include_str!("res/secretstore_acl_storage.json"); const SECRETSTORE_ACL_STORAGE_ABI: &'static str = include_str!("res/secretstore_acl_storage.json");
const VALIDATOR_SET_ABI: &'static str = include_str!("res/validator_set.json"); const VALIDATOR_SET_ABI: &'static str = include_str!("res/validator_set.json");
const VALIDATOR_REPORT_ABI: &'static str = include_str!("res/validator_report.json"); const VALIDATOR_REPORT_ABI: &'static str = include_str!("res/validator_report.json");
const PEER_SET_ABI: &'static str = include_str!("res/peer_set.json");
const TEST_VALIDATOR_SET_ABI: &'static str = include_str!("res/test_validator_set.json"); const TEST_VALIDATOR_SET_ABI: &'static str = include_str!("res/test_validator_set.json");
@ -53,6 +54,7 @@ fn main() {
build_file("SecretStoreAclStorage", SECRETSTORE_ACL_STORAGE_ABI, "secretstore_acl_storage.rs"); build_file("SecretStoreAclStorage", SECRETSTORE_ACL_STORAGE_ABI, "secretstore_acl_storage.rs");
build_file("ValidatorSet", VALIDATOR_SET_ABI, "validator_set.rs"); build_file("ValidatorSet", VALIDATOR_SET_ABI, "validator_set.rs");
build_file("ValidatorReport", VALIDATOR_REPORT_ABI, "validator_report.rs"); build_file("ValidatorReport", VALIDATOR_REPORT_ABI, "validator_report.rs");
build_file("PeerSet", PEER_SET_ABI, "peer_set.rs");
build_test_contracts(); build_test_contracts();
} }

View File

@ -0,0 +1 @@
[{"constant":true,"inputs":[{"name":"sl","type":"bytes32"},{"name":"sh","type":"bytes32"},{"name":"pl","type":"bytes32"},{"name":"ph","type":"bytes32"}],"name":"connectionAllowed","outputs":[{"name":"res","type":"bool"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]

View File

@ -30,6 +30,7 @@ mod service_transaction;
mod secretstore_acl_storage; mod secretstore_acl_storage;
mod validator_set; mod validator_set;
mod validator_report; mod validator_report;
mod peer_set;
pub mod test_contracts; pub mod test_contracts;
@ -40,3 +41,4 @@ pub use self::service_transaction::ServiceTransactionChecker;
pub use self::secretstore_acl_storage::SecretStoreAclStorage; pub use self::secretstore_acl_storage::SecretStoreAclStorage;
pub use self::validator_set::ValidatorSet; pub use self::validator_set::ValidatorSet;
pub use self::validator_report::ValidatorReport; pub use self::validator_report::ValidatorReport;
pub use self::peer_set::PeerSet;

View File

@ -0,0 +1,21 @@
// 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/>.
#![allow(unused_mut, unused_variables, unused_imports)]
//! Peer set contract.
include!(concat!(env!("OUT_DIR"), "/peer_set.rs"));

View File

@ -0,0 +1,16 @@
[package]
description = "Parity smart network connections"
homepage = "http://parity.io"
license = "GPL-3.0"
name = "node-filter"
version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
ethcore = { path = ".."}
ethcore-util = { path = "../../util" }
ethcore-io = { path = "../../util/io" }
ethcore-network = { path = "../../util/network" }
native-contracts = { path = "../native_contracts" }
futures = "0.1"
log = "0.3"

View File

@ -0,0 +1,44 @@
{
"name": "TestNodeFilterContract",
"engine": {
"authorityRound": {
"params": {
"stepDuration": 1,
"startStep": 2,
"validators": {
"contract": "0x0000000000000000000000000000000000000005"
}
}
}
},
"params": {
"accountStartNonce": "0x0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x69",
"gasLimitBoundDivisor": "0x0400"
},
"genesis": {
"seal": {
"generic": "0xc180"
},
"difficulty": "0x20000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x222222"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"0000000000000000000000000000000000000005": {
"balance": "1",
"constructor": "6060604052341561000f57600080fd5b5b6012600102600080601160010260001916815260200190815260200160002081600019169055506022600102600080602160010260001916815260200190815260200160002081600019169055506032600102600080603160010260001916815260200190815260200160002081600019169055506042600102600080604160010260001916815260200190815260200160002081600019169055505b5b610155806100bd6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063994d790a1461003e575b600080fd5b341561004957600080fd5b61008a6004808035600019169060200190919080356000191690602001909190803560001916906020019091908035600019169060200190919050506100a4565b604051808215151515815260200191505060405180910390f35b60006001800285600019161480156100c3575060026001028460001916145b156100d15760019050610121565b60006001028360001916141580156100f157506000600102826000191614155b801561011e5750816000191660008085600019166000191681526020019081526020016000205460001916145b90505b9493505050505600a165627a7a723058202082b8d8667fd397925f39785d8e804540beda0524d28af15921375145dfcc250029"
},
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
}
}

View File

@ -0,0 +1,154 @@
// 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/>.
//! Smart contract based node filter.
extern crate ethcore;
extern crate ethcore_util as util;
extern crate ethcore_network as network;
extern crate native_contracts;
extern crate futures;
#[cfg(test)] extern crate ethcore_io as io;
#[macro_use] extern crate log;
use std::sync::Weak;
use std::collections::HashMap;
use native_contracts::PeerSet as Contract;
use network::{NodeId, ConnectionFilter, ConnectionDirection};
use ethcore::client::{BlockChainClient, BlockId, ChainNotify};
use util::{Mutex, Address, H256, Bytes};
use futures::Future;
const MAX_CACHE_SIZE: usize = 4096;
/// Connection filter that uses a contract to manage permissions.
pub struct NodeFilter {
contract: Mutex<Option<Contract>>,
client: Weak<BlockChainClient>,
contract_address: Address,
permission_cache: Mutex<HashMap<NodeId, bool>>,
}
impl NodeFilter {
/// Create a new instance. Accepts a contract address.
pub fn new(client: Weak<BlockChainClient>, contract_address: Address) -> NodeFilter {
NodeFilter {
contract: Mutex::new(None),
client: client,
contract_address: contract_address,
permission_cache: Mutex::new(HashMap::new()),
}
}
/// Clear cached permissions.
pub fn clear_cache(&self) {
self.permission_cache.lock().clear();
}
}
impl ConnectionFilter for NodeFilter {
fn connection_allowed(&self, own_id: &NodeId, connecting_id: &NodeId, _direction: ConnectionDirection) -> bool {
let mut cache = self.permission_cache.lock();
if let Some(res) = cache.get(connecting_id) {
return *res;
}
let mut contract = self.contract.lock();
if contract.is_none() {
*contract = Some(Contract::new(self.contract_address));
}
let allowed = match (self.client.upgrade(), &*contract) {
(Some(ref client), &Some(ref contract)) => {
let own_low = H256::from_slice(&own_id[0..32]);
let own_high = H256::from_slice(&own_id[32..64]);
let id_low = H256::from_slice(&connecting_id[0..32]);
let id_high = H256::from_slice(&connecting_id[32..64]);
let allowed = contract.connection_allowed(
|addr, data| futures::done(client.call_contract(BlockId::Latest, addr, data)),
own_low,
own_high,
id_low,
id_high,
).wait().unwrap_or_else(|e| {
debug!("Error callling peer set contract: {:?}", e);
false
});
allowed
}
_ => false,
};
if cache.len() < MAX_CACHE_SIZE {
cache.insert(*connecting_id, allowed);
}
allowed
}
}
impl ChainNotify for NodeFilter {
fn new_blocks(&self, imported: Vec<H256>, _invalid: Vec<H256>, _enacted: Vec<H256>, _retracted: Vec<H256>, _sealed: Vec<H256>, _proposed: Vec<Bytes>, _duration: u64) {
if !imported.is_empty() {
self.clear_cache();
}
}
}
#[cfg(test)]
mod test {
use std::sync::{Arc, Weak};
use std::str::FromStr;
use ethcore::spec::Spec;
use ethcore::client::{BlockChainClient, Client, ClientConfig};
use ethcore::miner::Miner;
use util::{Address};
use network::{ConnectionDirection, ConnectionFilter, NodeId};
use io::IoChannel;
use super::NodeFilter;
/// Contract code: https://gist.github.com/arkpar/467dbcc73cbb85b0997a7a10ffa0695f
#[test]
fn node_filter() {
let contract_addr = Address::from_str("0000000000000000000000000000000000000005").unwrap();
let data = include_bytes!("../res/node_filter.json");
let spec = Spec::load(::std::env::temp_dir(), &data[..]).unwrap();
let client_db = Arc::new(::util::kvdb::in_memory(::ethcore::db::NUM_COLUMNS.unwrap_or(0)));
let client = Client::new(
ClientConfig::default(),
&spec,
client_db,
Arc::new(Miner::with_spec(&spec)),
IoChannel::disconnected(),
).unwrap();
let filter = NodeFilter::new(Arc::downgrade(&client) as Weak<BlockChainClient>, contract_addr);
let self1 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002").unwrap();
let self2 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003").unwrap();
let node1 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012").unwrap();
let node2 = NodeId::from_str("00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022").unwrap();
let nodex = NodeId::from_str("77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
assert!(filter.connection_allowed(&self1, &node1, ConnectionDirection::Inbound));
assert!(filter.connection_allowed(&self1, &nodex, ConnectionDirection::Inbound));
filter.clear_cache();
assert!(filter.connection_allowed(&self2, &node1, ConnectionDirection::Inbound));
assert!(filter.connection_allowed(&self2, &node2, ConnectionDirection::Inbound));
assert!(!filter.connection_allowed(&self2, &nodex, ConnectionDirection::Inbound));
}
}

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

View File

@ -748,7 +748,7 @@ impl Client {
self.factories.clone(), self.factories.clone(),
).expect("state known to be available for just-imported block; qed"); ).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) let res = Executive::new(&mut state, &env_info, &*self.engine)
.transact(&transaction, options); .transact(&transaction, options);
@ -1113,18 +1113,39 @@ impl Client {
}.fake_sign(from) }.fake_sign(from)
} }
fn do_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> { fn do_virtual_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 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, engine).transact_virtual(transaction, options)?;
let mut ret = Executive::new(state, env_info, &*self.engine).transact_virtual(t, options)?;
// TODO gav move this into Executive. if let Some(original) = original_state {
if let Some(original) = original_state { ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
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. // that's just a copy of the state.
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; 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> { 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()); let mut results = Vec::with_capacity(transactions.len());
for &(ref t, analytics) in transactions { 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; env_info.gas_used = ret.cumulative_gas_used;
results.push(ret); results.push(ret);
} }
@ -1189,7 +1210,7 @@ impl BlockChainClient for Client {
// that's just a copy of the state. // that's just a copy of the state.
let original_state = self.state_at(block).ok_or(CallError::StatePruned)?; let original_state = self.state_at(block).ok_or(CallError::StatePruned)?;
let sender = t.sender(); let sender = t.sender();
let options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false }; let options = || TransactOptions::with_tracing();
let cond = |gas| { let cond = |gas| {
let mut tx = t.as_unsigned().clone(); let mut tx = t.as_unsigned().clone();
@ -1198,7 +1219,7 @@ impl BlockChainClient for Client {
let mut state = original_state.clone(); let mut state = original_state.clone();
Ok(Executive::new(&mut state, &env_info, &*self.engine) Ok(Executive::new(&mut state, &env_info, &*self.engine)
.transact_virtual(&tx, options.clone()) .transact_virtual(&tx, options())
.map(|r| r.exception.is_none()) .map(|r| r.exception.is_none())
.unwrap_or(false)) .unwrap_or(false))
}; };
@ -1254,22 +1275,17 @@ impl BlockChainClient for Client {
return Err(CallError::TransactionNotFound); 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"; const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed";
let rest = txs.split_off(address.index); let rest = txs.split_off(address.index);
for t in txs { for t in txs {
let t = SignedTransaction::new(t).expect(PROOF); 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; 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 first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
let t = SignedTransaction::new(first).expect(PROOF); 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)?; self.do_virtual_call(&env_info, &mut state, &t, analytics)
if let Some(original) = original_state {
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?)
}
Ok(ret)
} }
fn mode(&self) -> IpcMode { fn mode(&self) -> IpcMode {

View File

@ -18,9 +18,9 @@
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use util::{self, U256, journaldb, trie}; use util::{self, U256, H256, journaldb, trie};
use util::kvdb::{self, KeyValueDB}; 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 factory::Factories;
use evm::{self, VMType}; use evm::{self, VMType};
use vm::{self, ActionParams}; use vm::{self, ActionParams};
@ -33,9 +33,17 @@ pub enum EvmTestError {
/// EVM error. /// EVM error.
Evm(vm::Error), Evm(vm::Error),
/// Initialization error. /// Initialization error.
Initialization(::error::Error), ClientError(::error::Error),
/// Low-level database error. /// Low-level database error.
Database(String), 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 { impl fmt::Display for EvmTestError {
@ -45,52 +53,114 @@ impl fmt::Display for EvmTestError {
match *self { match *self {
Trie(ref err) => write!(fmt, "Trie: {}", err), Trie(ref err) => write!(fmt, "Trie: {}", err),
Evm(ref err) => write!(fmt, "EVM: {}", 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), Database(ref err) => write!(fmt, "DB: {}", err),
PostCondition(ref err) => write!(fmt, "{}", err),
} }
} }
} }
/// Simplified, single-block EVM test client. use ethereum;
pub struct EvmTestClient { use ethjson::state::test::ForkSpec;
state_db: state_db::StateDB,
factories: Factories, lazy_static! {
spec: spec::Spec, 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 { /// Simplified, single-block EVM test client.
/// Creates new EVM test client with in-memory DB initialized with genesis of given Spec. pub struct EvmTestClient<'a> {
pub fn new(spec: spec::Spec) -> Result<Self, EvmTestError> { state: state::State<state_db::StateDB>,
let factories = Factories { spec: &'a spec::Spec,
vm: evm::Factory::new(VMType::Interpreter, 5 * 1024), }
trie: trie::TrieFactory::new(trie::TrieSpec::Secure),
accountdb: Default::default(), impl<'a> EvmTestClient<'a> {
}; /// Converts a json spec definition into spec.
let db = Arc::new(kvdb::in_memory(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); pub fn spec_from_json(spec: &ForkSpec) -> Option<&'static spec::Spec> {
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); match *spec {
let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); ForkSpec::Frontier => Some(&*FRONTIER),
state_db = spec.ensure_db_good(state_db, &factories).map_err(EvmTestError::Initialization)?; ForkSpec::Homestead => Some(&*HOMESTEAD),
// Write DB ForkSpec::EIP150 => Some(&*EIP150),
{ ForkSpec::EIP158 => Some(&*EIP161),
let mut batch = kvdb::DBTransaction::new(); ForkSpec::Metropolis | ForkSpec::Byzantium | ForkSpec::Constantinople => None,
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)?;
} }
}
/// 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 { Ok(EvmTestClient {
state_db, state,
factories,
spec, 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) pub fn call<T: trace::VMTracer>(&mut self, params: ActionParams, vm_tracer: &mut T)
-> Result<(U256, Vec<u8>), EvmTestError> -> Result<(U256, Vec<u8>), EvmTestError>
{ {
let genesis = self.spec.genesis_header(); 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 { let info = client::EnvInfo {
number: genesis.number(), number: genesis.number(),
author: *genesis.author(), author: *genesis.author(),
@ -103,7 +173,7 @@ impl EvmTestClient {
let mut substate = state::Substate::new(); let mut substate = state::Substate::new();
let mut tracer = trace::NoopTracer; let mut tracer = trace::NoopTracer;
let mut output = vec![]; 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( let (gas_left, _) = executive.call(
params, params,
&mut substate, &mut substate,
@ -114,4 +184,59 @@ impl EvmTestClient {
Ok((gas_left, output)) 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::client::*;
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType}; pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType};
pub use self::error::Error; 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::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::chain_notify::ChainNotify; pub use self::chain_notify::ChainNotify;
pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient}; 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`. /// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, header: &Header) -> BTreeMap<String, String> { 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 { 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 vm::{self, Ext, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue};
use wasm; use wasm;
use externalities::*; use externalities::*;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use trace::{self, FlatTrace, VMTrace, Tracer, VMTracer};
use transaction::{Action, SignedTransaction}; use transaction::{Action, SignedTransaction};
use crossbeam; use crossbeam;
pub use executed::{Executed, ExecutionResult}; pub use executed::{Executed, ExecutionResult};
@ -66,16 +66,77 @@ pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address,
} }
/// Transaction execution options. /// Transaction execution options.
#[derive(Default, Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct TransactOptions { pub struct TransactOptions<T, V> {
/// Enable call tracing. /// Enable call tracing.
pub tracing: bool, pub tracer: T,
/// Enable VM tracing. /// Enable VM tracing.
pub vm_tracing: bool, pub vm_tracer: V,
/// Check transaction nonce before execution. /// Check transaction nonce before execution.
pub check_nonce: bool, 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) pub fn executor<E>(engine: &E, vm_factory: &Factory, params: &ActionParams)
-> Box<vm::Vm> where E: Engine + ?Sized -> 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. /// This function should be used to execute transaction.
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> { pub fn transact<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
let check = options.check_nonce; -> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
match options.tracing { {
true => match options.vm_tracing { self.transact_with_tracer(t, options.check_nonce, options.tracer, options.vm_tracer)
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),
},
}
} }
/// Execute a transaction in a "virtual" context. /// Execute a transaction in a "virtual" context.
/// This will ensure the caller has enough balance to execute the desired transaction. /// 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 /// 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 sender = t.sender();
let balance = self.state.balance(&sender)?; let balance = self.state.balance(&sender)?;
let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price)); 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 /// Execute transaction/call with tracing enabled
pub fn transact_with_tracer<T, V>( fn transact_with_tracer<T, V>(
&'a mut self, &'a mut self,
t: &SignedTransaction, t: &SignedTransaction,
check_nonce: bool, check_nonce: bool,
@ -261,7 +316,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
}; };
// finalize here! // 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>( 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); trace!(target: "executive", "res={:?}", res);
let traces = subtracer.traces(); let traces = subtracer.drain();
match res { match res {
Ok(ref res) => tracer.trace_call( Ok(ref res) => tracer.trace_call(
trace_info, trace_info,
@ -484,9 +539,9 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
gas - res.gas_left, gas - res.gas_left,
trace_output, trace_output,
created, 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); 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 { let expected_vm_trace = VMTrace {
parent_step: 0, 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 { let expected_vm_trace = VMTrace {
parent_step: 0, parent_step: 0,
@ -1138,7 +1193,7 @@ mod tests {
let executed = { let executed = {
let mut ex = Executive::new(&mut state, &info, &engine); 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() ex.transact(&t, opts).unwrap()
}; };
@ -1175,7 +1230,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine); 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) ex.transact(&t, opts)
}; };
@ -1208,7 +1263,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine); 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) ex.transact(&t, opts)
}; };
@ -1241,7 +1296,7 @@ mod tests {
let res = { let res = {
let mut ex = Executive::new(&mut state, &info, &engine); 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) ex.transact(&t, opts)
}; };

View File

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

@ -506,8 +506,6 @@ pub struct AccountDetails {
pub balance: U256, pub balance: U256,
} }
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
const GAS_LIMIT_HYSTERESIS: usize = 200; // (100/GAS_LIMIT_HYSTERESIS) %
/// Transaction with the same (sender, nonce) can be replaced only if /// Transaction with the same (sender, nonce) can be replaced only if
/// `new_gas_price > old_gas_price + old_gas_price >> SHIFT` /// `new_gas_price > old_gas_price + old_gas_price >> SHIFT`
const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25% const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25%
@ -570,8 +568,8 @@ pub struct TransactionQueue {
minimal_gas_price: U256, minimal_gas_price: U256,
/// The maximum amount of gas any individual transaction may use. /// The maximum amount of gas any individual transaction may use.
tx_gas_limit: U256, tx_gas_limit: U256,
/// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0) /// Current gas limit (block gas limit). Transactions above the limit will not be accepted (default to !0)
total_gas_limit: U256, block_gas_limit: U256,
/// Maximal time transaction may occupy the queue. /// Maximal time transaction may occupy the queue.
/// When we reach `max_time_in_queue / 2^3` we re-validate /// When we reach `max_time_in_queue / 2^3` we re-validate
/// account balance. /// account balance.
@ -631,7 +629,7 @@ impl TransactionQueue {
TransactionQueue { TransactionQueue {
strategy, strategy,
minimal_gas_price: U256::zero(), minimal_gas_price: U256::zero(),
total_gas_limit: !U256::zero(), block_gas_limit: !U256::zero(),
tx_gas_limit, tx_gas_limit,
max_time_in_queue: DEFAULT_QUEUING_PERIOD, max_time_in_queue: DEFAULT_QUEUING_PERIOD,
current, current,
@ -674,16 +672,10 @@ impl TransactionQueue {
self.current.gas_price_entry_limit() self.current.gas_price_entry_limit()
} }
/// Sets new gas limit. Transactions with gas slightly (`GAS_LIMIT_HYSTERESIS`) above the limit won't be imported. /// Sets new gas limit. Transactions with gas over the limit will not be accepted.
/// Any transaction already imported to the queue is not affected. /// Any transaction already imported to the queue is not affected.
pub fn set_gas_limit(&mut self, gas_limit: U256) { pub fn set_gas_limit(&mut self, gas_limit: U256) {
let extra = gas_limit / U256::from(GAS_LIMIT_HYSTERESIS); self.block_gas_limit = gas_limit;
let total_gas_limit = match gas_limit.overflowing_add(extra) {
(_, true) => !U256::zero(),
(val, false) => val,
};
self.total_gas_limit = total_gas_limit;
} }
/// Sets new total gas limit. /// Sets new total gas limit.
@ -819,13 +811,13 @@ impl TransactionQueue {
})); }));
} }
let gas_limit = cmp::min(self.tx_gas_limit, self.total_gas_limit); let gas_limit = cmp::min(self.tx_gas_limit, self.block_gas_limit);
if tx.gas > gas_limit { if tx.gas > gas_limit {
trace!(target: "txqueue", trace!(target: "txqueue",
"Dropping transaction above gas limit: {:?} ({} > min({}, {}))", "Dropping transaction above gas limit: {:?} ({} > min({}, {}))",
tx.hash(), tx.hash(),
tx.gas, tx.gas,
self.total_gas_limit, self.block_gas_limit,
self.tx_gas_limit self.tx_gas_limit
); );
return Err(Error::Transaction(TransactionError::GasLimitExceeded { return Err(Error::Transaction(TransactionError::GasLimitExceeded {
@ -1922,13 +1914,13 @@ pub mod test {
// given // given
let mut txq = TransactionQueue::default(); let mut txq = TransactionQueue::default();
txq.set_gas_limit(U256::zero()); txq.set_gas_limit(U256::zero());
assert_eq!(txq.total_gas_limit, U256::zero()); assert_eq!(txq.block_gas_limit, U256::zero());
// when // when
txq.set_gas_limit(!U256::zero()); txq.set_gas_limit(!U256::zero());
// then // then
assert_eq!(txq.total_gas_limit, !U256::zero()); assert_eq!(txq.block_gas_limit, !U256::zero());
} }
#[test] #[test]
@ -1945,7 +1937,7 @@ pub mod test {
// then // then
assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded { assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded {
limit: U256::from(50_250), // Should be 100.5% of set_gas_limit limit: U256::from(50_000),
got: gas, got: gas,
}); });
let stats = txq.status(); let stats = txq.status();

View File

@ -99,6 +99,8 @@ pub struct CommonParams {
pub block_reward: U256, pub block_reward: U256,
/// Registrar contract address. /// Registrar contract address.
pub registrar: Address, pub registrar: Address,
/// Node permission managing contract address.
pub node_permission_contract: Option<Address>,
} }
impl CommonParams { impl CommonParams {
@ -170,6 +172,7 @@ impl From<ethjson::spec::Params> for CommonParams {
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
block_reward: p.block_reward.map_or_else(U256::zero, Into::into), block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
registrar: p.registrar.map_or_else(Address::new, Into::into), registrar: p.registrar.map_or_else(Address::new, Into::into),
node_permission_contract: p.node_permission_contract.map(Into::into),
} }
} }
} }

View File

@ -31,7 +31,7 @@ use vm::EnvInfo;
use error::Error; use error::Error;
use executive::{Executive, TransactOptions}; use executive::{Executive, TransactOptions};
use factory::Factories; use factory::Factories;
use trace::FlatTrace; use trace::{self, FlatTrace, VMTrace};
use pod_account::*; use pod_account::*;
use pod_state::{self, PodState}; use pod_state::{self, PodState};
use types::basic_account::BasicAccount; use types::basic_account::BasicAccount;
@ -59,8 +59,12 @@ pub use self::substate::Substate;
pub struct ApplyOutcome { pub struct ApplyOutcome {
/// The receipt for the applied transaction. /// The receipt for the applied transaction.
pub receipt: Receipt, 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>, 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. /// Result type for the execution ("application") of a transaction.
@ -205,7 +209,7 @@ pub fn check_proof(
Err(_) => return ProvedExecution::BadProof, 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), Ok(executed) => ProvedExecution::Complete(executed),
Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof,
Err(e) => ProvedExecution::Failed(e), Err(e) => ProvedExecution::Failed(e),
@ -239,7 +243,8 @@ pub fn prove_transaction<H: AsHashDB + Send + Sync>(
Err(_) => return None, Err(_) => return None,
}; };
match state.execute(env_info, engine, transaction, false, virt) { let options = TransactOptions::with_no_tracing().dont_check_nonce();
match state.execute(env_info, engine, transaction, options, virt) {
Err(ExecutionError::Internal(_)) => None, Err(ExecutionError::Internal(_)) => None,
Err(e) => { Err(e) => {
trace!(target: "state", "Proved call failed: {}", e); trace!(target: "state", "Proved call failed: {}", e);
@ -327,7 +332,7 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v
impl<B: Backend> State<B> { impl<B: Backend> State<B> {
/// Creates new state with empty state root /// 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> { pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State<B> {
let mut root = H256::new(); let mut root = H256::new();
{ {
@ -660,29 +665,57 @@ impl<B: Backend> State<B> {
/// Execute a given transaction, producing a receipt and an optional trace. /// Execute a given transaction, producing a receipt and an optional trace.
/// This will change the state accordingly. /// This will change the state accordingly.
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult { 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 { let state_root = if env_info.number < engine.params().eip98_transition || env_info.number < engine.params().validate_receipts_transition {
self.commit()?; self.commit()?;
Some(self.root().clone()) Some(self.root().clone())
} else { } else {
None None
}; };
let output = e.output;
let receipt = Receipt::new(state_root, e.cumulative_gas_used, e.logs); let receipt = Receipt::new(state_root, e.cumulative_gas_used, e.logs);
trace!(target: "state", "Transaction receipt: {:?}", receipt); 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. // Execute a given transaction without committing changes.
// //
// `virt` signals that we are executing outside of a block set and restrictions like // `virt` signals that we are executing outside of a block set and restrictions like
// gas limits and gas costs should be lifted. // gas limits and gas costs should be lifted.
fn execute(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool, virt: bool) fn execute<T, V>(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, options: TransactOptions<T, V>, virt: bool)
-> Result<Executed, ExecutionError> -> 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); let mut e = Executive::new(self, env_info, engine);
match virt { match virt {
@ -767,9 +800,8 @@ impl<B: Backend> State<B> {
Ok(()) Ok(())
} }
#[cfg(test)]
#[cfg(feature = "json-tests")]
/// Populate the state from `accounts`. /// Populate the state from `accounts`.
/// Used for tests.
pub fn populate_from(&mut self, accounts: PodState) { pub fn populate_from(&mut self, accounts: PodState) {
assert!(self.checkpoints.borrow().is_empty()); assert!(self.checkpoints.borrow().is_empty());
for (add, acc) in accounts.drain().into_iter() { for (add, acc) in accounts.drain().into_iter() {

View File

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

View File

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

View File

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

View File

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

View File

@ -61,6 +61,7 @@ pub struct FakeExt {
pub info: EnvInfo, pub info: EnvInfo,
pub schedule: Schedule, pub schedule: Schedule,
pub balances: HashMap<Address, U256>, pub balances: HashMap<Address, U256>,
pub tracing: bool,
} }
// similar to the normal `finalize` function, but ignoring NeedsReturn. // similar to the normal `finalize` function, but ignoring NeedsReturn.
@ -184,4 +185,8 @@ impl Ext for FakeExt {
fn inc_sstore_clears(&mut self) { fn inc_sstore_clears(&mut self) {
self.sstore_clears += 1; self.sstore_clears += 1;
} }
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool {
self.tracing
}
} }

View File

@ -415,36 +415,121 @@ fn storage_read() {
assert_eq!(Address::from(&result[12..32]), address); 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 let mut params = ActionParams::default();
// Test prepopulates address into storage, than executes a contract which read that address from storage and write this address into result 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] #[test]
fn math_add() { fn math_add() {
::ethcore_logger::init_log();
let code = load_sample!("math.wasm");
let mut params = ActionParams::default(); let (gas_left, result) = reqrep_test!(
params.gas = U256::from(100_000); "math.wasm",
params.code = Some(Arc::new(code)); {
let mut args = [0u8; 65];
let mut args = [0u8; 64]; let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap();
let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap(); let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap();
let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap(); arg_a.to_big_endian(&mut args[1..33]);
arg_a.to_big_endian(&mut args[0..32]); arg_b.to_big_endian(&mut args[33..65]);
arg_b.to_big_endian(&mut args[32..64]); args.to_vec()
params.data = Some(args.to_vec());
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut FakeExt::new()).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("storage_read should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
} }
}; );
let sum: U256 = (&result[..]).into(); assert_eq!(gas_left, U256::from(98087));
assert_eq!(
assert_eq!(gas_left, U256::from(96284)); U256::from_dec_str("1888888888888888888888888888887").unwrap(),
assert_eq!(sum, 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 = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
ethcore = { path = "../ethcore" } ethcore = { path = "../ethcore" }
ethjson = { path = "../json" }
ethcore-util = { path = "../util" } ethcore-util = { path = "../util" }
evm = { path = "../ethcore/evm" } evm = { path = "../ethcore/evm" }
vm = { path = "../ethcore/vm" } vm = { path = "../ethcore/vm" }

View File

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

View File

@ -27,7 +27,11 @@ use info as vm;
pub struct Informant; pub struct Informant;
impl vm::Informant for 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 { match result {
Ok(success) => { Ok(success) => {
println!("Output: 0x{}", success.output.to_hex()); println!("Output: 0x{}", success.output.to_hex());

View File

@ -17,17 +17,19 @@
//! VM runner. //! VM runner.
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
use util::U256; use util::{U256, H256};
use ethcore::{trace, spec}; use ethcore::{trace, spec, transaction, pod_state};
use ethcore::client::{EvmTestClient, EvmTestError}; use ethcore::client::{self, EvmTestClient, EvmTestError, TransactResult};
use vm::ActionParams; use ethjson;
/// VM execution informant /// VM execution informant
pub trait Informant: trace::VMTracer { pub trait Informant: trace::VMTracer {
/// Display a single run init message
fn before_test(&self, test: &str, action: &str);
/// Set initial gas. /// Set initial gas.
fn set_gas(&mut self, _gas: U256) {} fn set_gas(&mut self, _gas: U256) {}
/// Display final result. /// Display final result.
fn finish(&mut self, result: Result<Success, Failure>); fn finish(result: Result<Success, Failure>);
} }
/// Execution finished correctly /// Execution finished correctly
@ -50,17 +52,71 @@ pub struct Failure {
pub time: Duration, 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` /// Execute VM with given `ActionParams`
pub fn run<T: trace::VMTracer>(vm_tracer: &mut T, spec: spec::Spec, params: ActionParams) -> Result<Success, Failure> { pub fn run<'a, F, T>(spec: &'a spec::Spec, initial_gas: U256, pre_state: T, run: F) -> Result<Success, Failure> where
let mut test_client = EvmTestClient::new(spec).map_err(|error| Failure { 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(), gas_used: 0.into(),
error, error,
time: Duration::from_secs(0) time: Duration::from_secs(0)
})?; })?;
let initial_gas = params.gas;
let start = Instant::now(); let start = Instant::now();
let result = test_client.call(params, vm_tracer); let result = run(test_client);
let duration = start.elapsed(); let duration = start.elapsed();
match result { match result {

View File

@ -17,8 +17,9 @@
//! Parity EVM interpreter binary. //! Parity EVM interpreter binary.
#![warn(missing_docs)] #![warn(missing_docs)]
#![allow(dead_code)]
extern crate ethcore; extern crate ethcore;
extern crate ethjson;
extern crate rustc_hex; extern crate rustc_hex;
extern crate serde; extern crate serde;
#[macro_use] #[macro_use]
@ -31,6 +32,7 @@ extern crate panic_hook;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, fs}; use std::{fmt, fs};
use std::path::PathBuf;
use docopt::Docopt; use docopt::Docopt;
use rustc_hex::FromHex; use rustc_hex::FromHex;
use util::{U256, Bytes, Address}; use util::{U256, Bytes, Address};
@ -47,6 +49,7 @@ EVM implementation for Parity.
Copyright 2016, 2017 Parity Technologies (UK) Ltd Copyright 2016, 2017 Parity Technologies (UK) Ltd
Usage: Usage:
parity-evm state-test <file> [--json --only NAME --chain CHAIN]
parity-evm stats [options] parity-evm stats [options]
parity-evm [options] parity-evm [options]
parity-evm [-h | --help] parity-evm [-h | --help]
@ -59,6 +62,10 @@ Transaction options:
--gas GAS Supplied gas as hex (without 0x). --gas GAS Supplied gas as hex (without 0x).
--gas-price WEI Supplied gas price 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: General options:
--json Display verbose results in JSON. --json Display verbose results in JSON.
--chain CHAIN Chain spec file path. --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()); let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());
if args.flag_json { if args.cmd_state_test {
run(args, display::json::Informant::default()) run_state_test(args)
} else if args.flag_json {
run_call(args, display::json::Informant::default())
} else { } 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 from = arg(args.from(), "--from");
let to = arg(args.to(), "--to"); let to = arg(args.to(), "--to");
let code = arg(args.code(), "--code"); let code = arg(args.code(), "--code");
let spec = arg(args.spec(), "--chain"); let spec = arg(args.spec(), "--chain");
let gas = arg(args.gas(), "--gas"); 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"); let data = arg(args.data(), "--input");
if code.is_none() && to == Address::default() { if code.is_none() && to == Address::default() {
@ -103,13 +157,18 @@ fn run<T: Informant>(args: Args, mut informant: T) {
params.data = data; params.data = data;
informant.set_gas(gas); informant.set_gas(gas);
let result = info::run(&mut informant, spec, params); let result = info::run(&spec, gas, None, |mut client| {
informant.finish(result); client.call(params, &mut informant)
});
T::finish(result);
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct Args { struct Args {
cmd_stats: bool, cmd_stats: bool,
cmd_state_test: bool,
arg_file: Option<PathBuf>,
flag_only: Option<String>,
flag_from: Option<String>, flag_from: Option<String>,
flag_to: Option<String>, flag_to: Option<String>,
flag_code: Option<String>, flag_code: Option<String>,
@ -221,4 +280,22 @@ mod tests {
assert_eq!(args.data(), Ok(Some(vec![06]))); assert_eq!(args.data(), Ok(Some(vec![06])));
assert_eq!(args.flag_chain, Some("./testfile".to_owned())); 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

@ -119,7 +119,7 @@ mod tests {
use ethcore::client::TestBlockChainClient; use ethcore::client::TestBlockChainClient;
fn get_mocked_handler() -> IpfsHandler { fn get_mocked_handler() -> IpfsHandler {
IpfsHandler::new(None, None, Arc::new(TestBlockChainClient::new())) IpfsHandler::new(None.into(), None.into(), Arc::new(TestBlockChainClient::new()))
} }
#[test] #[test]

2
js/package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "parity.js", "name": "parity.js",
"version": "1.8.16", "version": "1.8.17",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "parity.js", "name": "parity.js",
"version": "1.8.16", "version": "1.8.17",
"main": "release/index.js", "main": "release/index.js",
"jsnext:main": "src/index.js", "jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>", "author": "Parity Team <admin@parity.io>",

View File

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

View File

@ -200,7 +200,7 @@ export default class Status {
const statusPromises = [ const statusPromises = [
this._api.eth.syncing(), this._api.eth.syncing(),
this._api.parity.netPeers(), this._api.parity.netPeers(),
this._fetchHealth() this._api.parity.nodeHealth()
]; ];
return Promise 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 * The data fetched here should not change
* unless Parity is restarted. They are thus * unless Parity is restarted. They are thus

View File

@ -53,7 +53,7 @@
.infoline, .infoline,
.uuidline { .uuidline {
opacity: 0.25; opacity: 0.5;
} }
.uuidline { .uuidline {

View File

@ -102,6 +102,9 @@ pub struct Params {
pub block_reward: Option<Uint>, pub block_reward: Option<Uint>,
/// See `CommonParams` docs. /// See `CommonParams` docs.
pub registrar: Option<Address>, pub registrar: Option<Address>,
/// Node permission contract address.
#[serde(rename="nodePermissionContract")]
pub node_permission_contract: Option<Address>,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -104,7 +104,10 @@ pub enum ForkSpec {
EIP158, EIP158,
Frontier, Frontier,
Homestead, Homestead,
// TODO [ToDr] Deprecated
Metropolis, Metropolis,
Byzantium,
Constantinople,
} }
/// State test indexes deserialization. /// State test indexes deserialization.
@ -161,7 +164,7 @@ mod tests {
"EIP150" : [ "EIP150" : [
{ {
"hash" : "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", "hash" : "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f",
"indexes" : { "data" : 0, "gas" : 0, "value" : 0 } "indexes" : { "data" : 0, "gas" : 0, "value" : 0 }
}, },
{ {
"hash" : "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", "hash" : "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764",

View File

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

View File

@ -22,12 +22,12 @@ use ethcore::client::{Client, BlockChainClient, BlockId};
use ethcore::transaction::{Transaction, Action}; use ethcore::transaction::{Transaction, Action};
use ethsync::LightSync; use ethsync::LightSync;
use futures::{future, IntoFuture, Future, BoxFuture}; use futures::{future, IntoFuture, Future, BoxFuture};
use futures_cpupool::CpuPool;
use hash_fetch::fetch::Client as FetchClient; use hash_fetch::fetch::Client as FetchClient;
use hash_fetch::urlhint::ContractClient; use hash_fetch::urlhint::ContractClient;
use helpers::replace_home; use helpers::replace_home;
use light::client::LightChainClient; use light::client::LightChainClient;
use light::on_demand::{self, OnDemand}; use light::on_demand::{self, OnDemand};
use node_health::{SyncStatus, NodeHealth};
use rpc; use rpc;
use rpc_apis::SignerService; use rpc_apis::SignerService;
use parity_reactor; use parity_reactor;
@ -36,7 +36,6 @@ use util::{Bytes, Address};
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Configuration { pub struct Configuration {
pub enabled: bool, pub enabled: bool,
pub ntp_servers: Vec<String>,
pub dapps_path: PathBuf, pub dapps_path: PathBuf,
pub extra_dapps: Vec<PathBuf>, pub extra_dapps: Vec<PathBuf>,
pub extra_embed_on: Vec<(String, u16)>, pub extra_embed_on: Vec<(String, u16)>,
@ -48,12 +47,6 @@ impl Default for Configuration {
let data_dir = default_data_path(); let data_dir = default_data_path();
Configuration { Configuration {
enabled: true, 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(), dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
extra_dapps: vec![], extra_dapps: vec![],
extra_embed_on: vec![], extra_embed_on: vec![],
@ -156,10 +149,10 @@ impl<T: LightChainClient + 'static> ContractClient for LightRegistrar<T> {
// to resolve. // to resolve.
#[derive(Clone)] #[derive(Clone)]
pub struct Dependencies { pub struct Dependencies {
pub node_health: NodeHealth,
pub sync_status: Arc<SyncStatus>, pub sync_status: Arc<SyncStatus>,
pub contract_client: Arc<ContractClient>, pub contract_client: Arc<ContractClient>,
pub remote: parity_reactor::TokioRemote, pub remote: parity_reactor::TokioRemote,
pub pool: CpuPool,
pub fetch: FetchClient, pub fetch: FetchClient,
pub signer: Arc<SignerService>, pub signer: Arc<SignerService>,
pub ui_address: Option<(String, u16)>, pub ui_address: Option<(String, u16)>,
@ -172,7 +165,6 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Mi
server::dapps_middleware( server::dapps_middleware(
deps, deps,
&configuration.ntp_servers,
configuration.dapps_path, configuration.dapps_path,
configuration.extra_dapps, configuration.extra_dapps,
rpc::DAPPS_DOMAIN, rpc::DAPPS_DOMAIN,
@ -181,19 +173,18 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Mi
).map(Some) ).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 { if !enabled {
return Ok(None); return Ok(None);
} }
server::ui_middleware( server::ui_middleware(
deps, deps,
ntp_servers,
rpc::DAPPS_DOMAIN, rpc::DAPPS_DOMAIN,
).map(Some) ).map(Some)
} }
pub use self::server::{SyncStatus, Middleware, service}; pub use self::server::{Middleware, service};
#[cfg(not(feature = "dapps"))] #[cfg(not(feature = "dapps"))]
mod server { mod server {
@ -203,11 +194,6 @@ mod server {
use parity_rpc::{hyper, RequestMiddleware, RequestMiddlewareAction}; use parity_rpc::{hyper, RequestMiddleware, RequestMiddlewareAction};
use rpc_apis; use rpc_apis;
pub trait SyncStatus {
fn is_major_importing(&self) -> bool;
fn peers(&self) -> (usize, usize);
}
pub struct Middleware; pub struct Middleware;
impl RequestMiddleware for Middleware { impl RequestMiddleware for Middleware {
fn on_request( fn on_request(
@ -219,7 +205,6 @@ mod server {
pub fn dapps_middleware( pub fn dapps_middleware(
_deps: Dependencies, _deps: Dependencies,
_ntp_servers: &[String],
_dapps_path: PathBuf, _dapps_path: PathBuf,
_extra_dapps: Vec<PathBuf>, _extra_dapps: Vec<PathBuf>,
_dapps_domain: &str, _dapps_domain: &str,
@ -231,7 +216,6 @@ mod server {
pub fn ui_middleware( pub fn ui_middleware(
_deps: Dependencies, _deps: Dependencies,
_ntp_servers: &[String],
_dapps_domain: &str, _dapps_domain: &str,
) -> Result<Middleware, String> { ) -> Result<Middleware, String> {
Err("Your Parity version has been compiled without UI support.".into()) Err("Your Parity version has been compiled without UI support.".into())
@ -253,11 +237,9 @@ mod server {
use parity_reactor; use parity_reactor;
pub use parity_dapps::Middleware; pub use parity_dapps::Middleware;
pub use parity_dapps::SyncStatus;
pub fn dapps_middleware( pub fn dapps_middleware(
deps: Dependencies, deps: Dependencies,
ntp_servers: &[String],
dapps_path: PathBuf, dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>, extra_dapps: Vec<PathBuf>,
dapps_domain: &str, dapps_domain: &str,
@ -269,8 +251,7 @@ mod server {
let web_proxy_tokens = Arc::new(move |token| signer.web_proxy_access_token_domain(&token)); let web_proxy_tokens = Arc::new(move |token| signer.web_proxy_access_token_domain(&token));
Ok(parity_dapps::Middleware::dapps( Ok(parity_dapps::Middleware::dapps(
ntp_servers, deps.node_health,
deps.pool,
parity_remote, parity_remote,
deps.ui_address, deps.ui_address,
extra_embed_on, extra_embed_on,
@ -287,13 +268,11 @@ mod server {
pub fn ui_middleware( pub fn ui_middleware(
deps: Dependencies, deps: Dependencies,
ntp_servers: &[String],
dapps_domain: &str, dapps_domain: &str,
) -> Result<Middleware, String> { ) -> Result<Middleware, String> {
let parity_remote = parity_reactor::Remote::new(deps.remote.clone()); let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
Ok(parity_dapps::Middleware::ui( Ok(parity_dapps::Middleware::ui(
ntp_servers, deps.node_health,
deps.pool,
parity_remote, parity_remote,
dapps_domain, dapps_domain,
deps.contract_client, deps.contract_client,

View File

@ -58,6 +58,7 @@ extern crate ethcore_util as util;
extern crate ethcore_network as network; extern crate ethcore_network as network;
extern crate ethkey; extern crate ethkey;
extern crate ethsync; extern crate ethsync;
extern crate node_health;
extern crate panic_hook; extern crate panic_hook;
extern crate parity_hash_fetch as hash_fetch; extern crate parity_hash_fetch as hash_fetch;
extern crate parity_ipfs_api; extern crate parity_ipfs_api;
@ -68,6 +69,7 @@ extern crate parity_updater as updater;
extern crate parity_whisper; extern crate parity_whisper;
extern crate path; extern crate path;
extern crate rpc_cli; extern crate rpc_cli;
extern crate node_filter;
#[macro_use] #[macro_use]
extern crate log as rlog; extern crate log as rlog;

View File

@ -19,7 +19,7 @@ use std::path::Path;
use ethcore::client::BlockChainClient; use ethcore::client::BlockChainClient;
use hypervisor::Hypervisor; use hypervisor::Hypervisor;
use ethsync::{AttachedProtocol, SyncConfig, NetworkConfiguration, NetworkError, Params}; use ethsync::{AttachedProtocol, SyncConfig, NetworkConfiguration, NetworkError, Params, ConnectionFilter};
use ethcore::snapshot::SnapshotService; use ethcore::snapshot::SnapshotService;
use light::Provider; use light::Provider;
@ -183,6 +183,7 @@ pub fn sync(
provider: Arc<Provider>, provider: Arc<Provider>,
_log_settings: &LogConfig, _log_settings: &LogConfig,
attached_protos: Vec<AttachedProtocol>, attached_protos: Vec<AttachedProtocol>,
connection_filter: Option<Arc<ConnectionFilter>>,
) -> Result<SyncModules, NetworkError> { ) -> Result<SyncModules, NetworkError> {
let eth_sync = EthSync::new(Params { let eth_sync = EthSync::new(Params {
config: sync_cfg, config: sync_cfg,
@ -191,7 +192,8 @@ pub fn sync(
snapshot_service: snapshot_service, snapshot_service: snapshot_service,
network_config: net_cfg, network_config: net_cfg,
attached_protos: attached_protos, attached_protos: attached_protos,
})?; },
connection_filter)?;
Ok((eth_sync.clone() as Arc<SyncProvider>, eth_sync.clone() as Arc<ManageNetwork>, eth_sync.clone() as Arc<ChainNotify>)) Ok((eth_sync.clone() as Arc<SyncProvider>, eth_sync.clone() as Arc<ManageNetwork>, eth_sync.clone() as Arc<ChainNotify>))
} }

View File

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

View File

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

View File

@ -14,6 +14,7 @@
// 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/>.
use std::fmt;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::net::{TcpListener}; use std::net::{TcpListener};
@ -32,10 +33,12 @@ use fdlimit::raise_fd_limit;
use hash_fetch::fetch::{Fetch, Client as FetchClient}; use hash_fetch::fetch::{Fetch, Client as FetchClient};
use informant::{Informant, LightNodeInformantData, FullNodeInformantData}; use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
use light::Cache as LightDataCache; use light::Cache as LightDataCache;
use node_health;
use parity_reactor::EventLoop; use parity_reactor::EventLoop;
use parity_rpc::{NetworkSettings, informant, is_major_importing}; use parity_rpc::{NetworkSettings, informant, is_major_importing};
use updater::{UpdatePolicy, Updater}; use updater::{UpdatePolicy, Updater};
use util::{Colour, version, Mutex, Condvar}; use util::{Colour, version, Mutex, Condvar};
use node_filter::NodeFilter;
use params::{ use params::{
SpecType, Pruning, AccountsConfig, GasPricerConfig, MinerExtras, Switch, SpecType, Pruning, AccountsConfig, GasPricerConfig, MinerExtras, Switch,
@ -80,6 +83,7 @@ pub struct RunCmd {
pub daemon: Option<String>, pub daemon: Option<String>,
pub logger_config: LogConfig, pub logger_config: LogConfig,
pub miner_options: MinerOptions, pub miner_options: MinerOptions,
pub ntp_servers: Vec<String>,
pub ws_conf: rpc::WsConfiguration, pub ws_conf: rpc::WsConfiguration,
pub http_conf: rpc::HttpConfiguration, pub http_conf: rpc::HttpConfiguration,
pub ipc_conf: rpc::IpcConfiguration, pub ipc_conf: rpc::IpcConfiguration,
@ -290,7 +294,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
// the dapps server // the dapps server
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.ui_conf, &cmd.logger_config)); 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 { let contract_client = Arc::new(::dapps::LightRegistrar {
client: service.client().clone(), client: service.client().clone(),
sync: light_sync.clone(), sync: light_sync.clone(),
@ -298,7 +302,12 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
}); });
struct LightSyncStatus(Arc<LightSync>); 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 is_major_importing(&self) -> bool { self.0.is_major_importing() }
fn peers(&self) -> (usize, usize) { fn peers(&self) -> (usize, usize) {
let peers = ethsync::LightSyncProvider::peer_numbers(&*self.0); let peers = ethsync::LightSyncProvider::peer_numbers(&*self.0);
@ -306,19 +315,26 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
} }
} }
dapps::Dependencies { let sync_status = Arc::new(LightSyncStatus(light_sync.clone()));
sync_status: Arc::new(LightSyncStatus(light_sync.clone())), let node_health = node_health::NodeHealth::new(
pool: fetch.pool(), 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, contract_client: contract_client,
remote: event_loop.raw_remote(), remote: event_loop.raw_remote(),
fetch: fetch.clone(), fetch: fetch.clone(),
signer: signer_service.clone(), signer: signer_service.clone(),
ui_address: cmd.ui_conf.address(), ui_address: cmd.ui_conf.address(),
} })
}; };
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?; 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 // start RPCs
let dapps_service = dapps::service(&dapps_middleware); let dapps_service = dapps::service(&dapps_middleware);
@ -327,6 +343,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
client: service.client().clone(), client: service.client().clone(),
sync: light_sync.clone(), sync: light_sync.clone(),
net: light_sync.clone(), net: light_sync.clone(),
health: node_health,
secret_store: account_provider, secret_store: account_provider,
logger: logger, logger: logger,
settings: Arc::new(cmd.net_settings), settings: Arc::new(cmd.net_settings),
@ -560,11 +577,13 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
miner.clone(), miner.clone(),
).map_err(|e| format!("Client service error: {:?}", e))?; ).map_err(|e| format!("Client service error: {:?}", e))?;
let connection_filter_address = spec.params().node_permission_contract;
// drop the spec to free up genesis state. // drop the spec to free up genesis state.
drop(spec); drop(spec);
// take handle to client // take handle to client
let client = service.client(); let client = service.client();
let connection_filter = connection_filter_address.map(|a| Arc::new(NodeFilter::new(Arc::downgrade(&client) as Weak<BlockChainClient>, a)));
let snapshot_service = service.snapshot_service(); let snapshot_service = service.snapshot_service();
// initialize the local node information store. // initialize the local node information store.
@ -636,9 +655,13 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
client.clone(), client.clone(),
&cmd.logger_config, &cmd.logger_config,
attached_protos, attached_protos,
connection_filter.clone().map(|f| f as Arc<::ethsync::ConnectionFilter + 'static>),
).map_err(|e| format!("Sync error: {}", e))?; ).map_err(|e| format!("Sync error: {}", e))?;
service.add_notify(chain_notify.clone()); service.add_notify(chain_notify.clone());
if let Some(filter) = connection_filter {
service.add_notify(filter);
}
// start network // start network
if network_enabled { if network_enabled {
@ -668,12 +691,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)); let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.ui_conf, &cmd.logger_config));
// the dapps server // the dapps server
let dapps_deps = { let (node_health, dapps_deps) = {
let (sync, client) = (sync_provider.clone(), client.clone()); let (sync, client) = (sync_provider.clone(), client.clone());
let contract_client = Arc::new(::dapps::FullRegistrar { client: client.clone() }); let contract_client = Arc::new(::dapps::FullRegistrar { client: client.clone() });
struct SyncStatus(Arc<ethsync::SyncProvider>, Arc<Client>, ethsync::NetworkConfiguration); 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 { fn is_major_importing(&self) -> bool {
is_major_importing(Some(self.0.status().state), self.1.queue_info()) is_major_importing(Some(self.0.status().state), self.1.queue_info())
} }
@ -683,18 +711,24 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
} }
} }
dapps::Dependencies { let sync_status = Arc::new(SyncStatus(sync, client, net_conf));
sync_status: Arc::new(SyncStatus(sync, client, net_conf)), let node_health = node_health::NodeHealth::new(
pool: fetch.pool(), 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, contract_client: contract_client,
remote: event_loop.raw_remote(), remote: event_loop.raw_remote(),
fetch: fetch.clone(), fetch: fetch.clone(),
signer: signer_service.clone(), signer: signer_service.clone(),
ui_address: cmd.ui_conf.address(), ui_address: cmd.ui_conf.address(),
} })
}; };
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?; 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 dapps_service = dapps::service(&dapps_middleware);
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies { let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
@ -702,6 +736,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
snapshot: snapshot_service.clone(), snapshot: snapshot_service.clone(),
client: client.clone(), client: client.clone(),
sync: sync_provider.clone(), sync: sync_provider.clone(),
health: node_health,
net: manage_network.clone(), net: manage_network.clone(),
secret_store: secret_store, secret_store: secret_store,
miner: miner.clone(), miner: miner.clone(),

View File

@ -47,12 +47,13 @@ ethjson = { path = "../json" }
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }
ethcore-light = { path = "../ethcore/light" } ethcore-light = { path = "../ethcore/light" }
ethcore-logger = { path = "../logger" } 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" } 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" } stats = { path = "../util/stats" }
vm = { path = "../ethcore/vm" }
clippy = { version = "0.0.103", optional = true} clippy = { version = "0.0.103", optional = true}
pretty_assertions = "0.1" pretty_assertions = "0.1"

View File

@ -55,6 +55,7 @@ extern crate ethsync;
extern crate ethcore_logger; extern crate ethcore_logger;
extern crate vm; extern crate vm;
extern crate fetch; extern crate fetch;
extern crate node_health;
extern crate parity_reactor; extern crate parity_reactor;
extern crate parity_updater as updater; extern crate parity_updater as updater;
extern crate rlp; 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. /// Extract the default gas price from a client and miner.
pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where
where C: MiningBlockChainClient, M: MinerService C: MiningBlockChainClient,
M: MinerService,
{ {
client.gas_price_corpus(100).median().cloned().unwrap_or_else(|| miner.sensible_gas_price()) client.gas_price_corpus(100).median().cloned().unwrap_or_else(|| miner.sensible_gas_price())
} }

View File

@ -24,21 +24,23 @@ use jsonrpc_core::Error;
use v1::helpers::CallRequest; use v1::helpers::CallRequest;
use v1::helpers::dispatch::default_gas_price; use v1::helpers::dispatch::default_gas_price;
pub fn sign_call<B: MiningBlockChainClient, M: MinerService>( pub fn sign_call<C: MiningBlockChainClient, M: MinerService> (
client: &Arc<B>, client: &Arc<C>,
miner: &Arc<M>, miner: &Arc<M>,
request: CallRequest, request: CallRequest,
gas_cap: bool, gas_cap: bool,
) -> Result<SignedTransaction, Error> { ) -> Result<SignedTransaction, Error> {
let from = request.from.unwrap_or(0.into()); let max_gas = 50_000_000.into();
let mut gas = request.gas.unwrap_or(U256::max_value()); let gas = match request.gas {
if gas_cap { Some(gas) if gas_cap && gas > max_gas => {
let max_gas = 50_000_000.into();
if gas > max_gas {
warn!("Gas limit capped to {} (from {})", max_gas, gas); warn!("Gas limit capped to {} (from {})", max_gas, gas);
gas = max_gas max_gas
} }
} Some(gas) => gas,
None if gas_cap => max_gas,
None => U256::from(2) << 50,
};
let from = request.from.unwrap_or(0.into());
Ok(Transaction { Ok(Transaction {
nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)), nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)),

View File

@ -19,15 +19,15 @@ use std::sync::Arc;
use std::collections::{BTreeMap, HashSet}; use std::collections::{BTreeMap, HashSet};
use futures::{future, Future, BoxFuture}; use futures::{future, Future, BoxFuture};
use ethcore_logger::RotatingLogger;
use util::misc::version_data; use util::misc::version_data;
use crypto::ecies; use crypto::{ecies, DEFAULT_MAC};
use ethkey::{Brain, Generator}; use ethkey::{Brain, Generator};
use ethstore::random_phrase; use ethstore::random_phrase;
use ethsync::LightSyncProvider; use ethsync::LightSyncProvider;
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use crypto::DEFAULT_MAC; use ethcore_logger::RotatingLogger;
use node_health::{NodeHealth, Health};
use light::client::LightChainClient; use light::client::LightChainClient;
@ -53,6 +53,7 @@ pub struct ParityClient {
accounts: Arc<AccountProvider>, accounts: Arc<AccountProvider>,
logger: Arc<RotatingLogger>, logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>, settings: Arc<NetworkSettings>,
health: NodeHealth,
signer: Option<Arc<SignerService>>, signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>, dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>, ws_address: Option<(String, u16)>,
@ -67,18 +68,20 @@ impl ParityClient {
accounts: Arc<AccountProvider>, accounts: Arc<AccountProvider>,
logger: Arc<RotatingLogger>, logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>, settings: Arc<NetworkSettings>,
health: NodeHealth,
signer: Option<Arc<SignerService>>, signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>, dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>, ws_address: Option<(String, u16)>,
) -> Self { ) -> Self {
ParityClient { ParityClient {
light_dispatch: light_dispatch, light_dispatch,
accounts: accounts, accounts,
logger: logger, logger,
settings: settings, settings,
signer: signer, health,
dapps_address: dapps_address, signer,
ws_address: ws_address, dapps_address,
ws_address,
eip86_transition: client.eip86_transition(), 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> { fn call(&self, _meta: Self::Metadata, _requests: Vec<CallRequest>, _block: Trailing<BlockNumber>) -> BoxFuture<Vec<Bytes>, Error> {
future::err(errors::light_unimplemented(None)).boxed() 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 std::collections::{BTreeMap, HashSet};
use futures::{future, Future, BoxFuture}; use futures::{future, Future, BoxFuture};
use ethcore_logger::RotatingLogger;
use util::Address; use util::Address;
use util::misc::version_data; use util::misc::version_data;
use crypto::ecies; use crypto::{DEFAULT_MAC, ecies};
use ethkey::{Brain, Generator}; use ethkey::{Brain, Generator};
use ethstore::random_phrase; use ethstore::random_phrase;
use ethsync::{SyncProvider, ManageNetwork}; use ethsync::{SyncProvider, ManageNetwork};
@ -34,8 +33,9 @@ use ethcore::ids::BlockId;
use ethcore::miner::MinerService; use ethcore::miner::MinerService;
use ethcore::mode::Mode; use ethcore::mode::Mode;
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethcore_logger::RotatingLogger;
use node_health::{NodeHealth, Health};
use updater::{Service as UpdateService}; use updater::{Service as UpdateService};
use crypto::DEFAULT_MAC;
use jsonrpc_core::Error; use jsonrpc_core::Error;
use jsonrpc_macros::Trailing; use jsonrpc_macros::Trailing;
@ -53,17 +53,13 @@ use v1::types::{
}; };
/// Parity implementation. /// Parity implementation.
pub struct ParityClient<C, M, S: ?Sized, U> where pub struct ParityClient<C, M, U> {
C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider,
U: UpdateService,
{
client: Arc<C>, client: Arc<C>,
miner: Arc<M>, miner: Arc<M>,
sync: Arc<S>,
updater: Arc<U>, updater: Arc<U>,
sync: Arc<SyncProvider>,
net: Arc<ManageNetwork>, net: Arc<ManageNetwork>,
health: NodeHealth,
accounts: Option<Arc<AccountProvider>>, accounts: Option<Arc<AccountProvider>>,
logger: Arc<RotatingLogger>, logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>, settings: Arc<NetworkSettings>,
@ -73,39 +69,39 @@ pub struct ParityClient<C, M, S: ?Sized, U> where
eip86_transition: u64, 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, C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider,
U: UpdateService,
{ {
/// Creates new `ParityClient`. /// Creates new `ParityClient`.
pub fn new( pub fn new(
client: &Arc<C>, client: Arc<C>,
miner: &Arc<M>, miner: Arc<M>,
sync: &Arc<S>, sync: Arc<SyncProvider>,
updater: &Arc<U>, updater: Arc<U>,
net: &Arc<ManageNetwork>, net: Arc<ManageNetwork>,
store: &Option<Arc<AccountProvider>>, health: NodeHealth,
accounts: Option<Arc<AccountProvider>>,
logger: Arc<RotatingLogger>, logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>, settings: Arc<NetworkSettings>,
signer: Option<Arc<SignerService>>, signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>, dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>, ws_address: Option<(String, u16)>,
) -> Self { ) -> Self {
let eip86_transition = client.eip86_transition();
ParityClient { ParityClient {
client: client.clone(), client,
miner: miner.clone(), miner,
sync: sync.clone(), sync,
updater: updater.clone(), updater,
net: net.clone(), net,
accounts: store.clone(), health,
logger: logger, accounts,
settings: settings, logger,
signer: signer, settings,
dapps_address: dapps_address, signer,
ws_address: ws_address, dapps_address,
eip86_transition: client.eip86_transition(), 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 impl<C, M, U> Parity for ParityClient<C, M, U> where
M: MinerService + 'static,
C: MiningBlockChainClient + 'static, C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static, M: MinerService + 'static,
U: UpdateService + 'static, U: UpdateService + 'static,
{ {
type Metadata = Metadata; 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() 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/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use std::sync::Arc;
use ethcore_logger::RotatingLogger;
use util::{Address, U256};
use ethsync::ManageNetwork;
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use ethcore::client::{TestBlockChainClient, Executed}; use ethcore::client::{TestBlockChainClient, Executed};
use ethcore::miner::LocalTransactionStatus; use ethcore::miner::LocalTransactionStatus;
use ethcore_logger::RotatingLogger;
use ethstore::ethkey::{Generator, Random}; use ethstore::ethkey::{Generator, Random};
use ethsync::ManageNetwork;
use node_health::{self, NodeHealth};
use parity_reactor;
use util::Address;
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use v1::{Parity, ParityClient}; use v1::{Parity, ParityClient};
@ -30,13 +32,14 @@ use v1::helpers::{SignerService, NetworkSettings};
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestUpdater}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestUpdater};
use super::manage_network::TestManageNetwork; use super::manage_network::TestManageNetwork;
pub type TestParityClient = ParityClient<TestBlockChainClient, TestMinerService, TestSyncProvider, TestUpdater>; pub type TestParityClient = ParityClient<TestBlockChainClient, TestMinerService, TestUpdater>;
pub struct Dependencies { pub struct Dependencies {
pub miner: Arc<TestMinerService>, pub miner: Arc<TestMinerService>,
pub client: Arc<TestBlockChainClient>, pub client: Arc<TestBlockChainClient>,
pub sync: Arc<TestSyncProvider>, pub sync: Arc<TestSyncProvider>,
pub updater: Arc<TestUpdater>, pub updater: Arc<TestUpdater>,
pub health: NodeHealth,
pub logger: Arc<RotatingLogger>, pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>, pub settings: Arc<NetworkSettings>,
pub network: Arc<ManageNetwork>, pub network: Arc<ManageNetwork>,
@ -54,6 +57,11 @@ impl Dependencies {
network_id: 3, network_id: 3,
num_peers: 120, 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()), updater: Arc::new(TestUpdater::default()),
logger: Arc::new(RotatingLogger::new("rpc=trace".to_owned())), logger: Arc::new(RotatingLogger::new("rpc=trace".to_owned())),
settings: Arc::new(NetworkSettings { settings: Arc::new(NetworkSettings {
@ -75,12 +83,13 @@ impl Dependencies {
let opt_accounts = Some(self.accounts.clone()); let opt_accounts = Some(self.accounts.clone());
ParityClient::new( ParityClient::new(
&self.client, self.client.clone(),
&self.miner, self.miner.clone(),
&self.sync, self.sync.clone(),
&self.updater, self.updater.clone(),
&self.network, self.network.clone(),
&opt_accounts, self.health.clone(),
opt_accounts.clone(),
self.logger.clone(), self.logger.clone(),
self.settings.clone(), self.settings.clone(),
signer, 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] #[test]
fn rpc_parity_accounts_info() { fn rpc_parity_accounts_info() {
let deps = Dependencies::new(); let deps = Dependencies::new();
@ -507,6 +523,8 @@ fn rpc_parity_cid() {
#[test] #[test]
fn rpc_parity_call() { fn rpc_parity_call() {
use util::U256;
let deps = Dependencies::new(); let deps = Dependencies::new();
deps.client.set_execution_result(Ok(Executed { deps.client.set_execution_result(Ok(Executed {
exception: None, exception: None,
@ -541,3 +559,14 @@ fn rpc_parity_call() {
assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); 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 jsonrpc_macros::Trailing;
use futures::BoxFuture; use futures::BoxFuture;
use node_health::Health;
use v1::types::{ use v1::types::{
H160, H256, H512, U256, Bytes, CallRequest, H160, H256, H512, U256, Bytes, CallRequest,
Peers, Transaction, RpcSettings, Histogram, Peers, Transaction, RpcSettings, Histogram,
@ -207,5 +208,9 @@ build_rpc_trait! {
/// Call contract, returning the output data. /// Call contract, returning the output data.
#[rpc(meta, name = "parity_call")] #[rpc(meta, name = "parity_call")]
fn call(&self, Self::Metadata, Vec<CallRequest>, Trailing<BlockNumber>) -> BoxFuture<Vec<Bytes>, Error>; 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

@ -19,7 +19,7 @@ use std::collections::{HashMap, BTreeMap};
use std::io; use std::io;
use util::Bytes; use util::Bytes;
use network::{NetworkProtocolHandler, NetworkService, NetworkContext, HostInfo, PeerId, ProtocolId, use network::{NetworkProtocolHandler, NetworkService, NetworkContext, HostInfo, PeerId, ProtocolId,
NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError}; NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError, ConnectionFilter};
use util::{U256, H256, H512}; use util::{U256, H256, H512};
use io::{TimerToken}; use io::{TimerToken};
use ethcore::ethstore::ethkey::Secret; use ethcore::ethstore::ethkey::Secret;
@ -236,7 +236,7 @@ pub struct EthSync {
impl EthSync { impl EthSync {
/// Creates and register protocol with the network service /// Creates and register protocol with the network service
pub fn new(params: Params) -> Result<Arc<EthSync>, NetworkError> { pub fn new(params: Params, connection_filter: Option<Arc<ConnectionFilter>>) -> Result<Arc<EthSync>, NetworkError> {
const MAX_LIGHTSERV_LOAD: f64 = 0.5; const MAX_LIGHTSERV_LOAD: f64 = 0.5;
let pruning_info = params.chain.pruning_info(); let pruning_info = params.chain.pruning_info();
@ -272,7 +272,7 @@ impl EthSync {
}; };
let chain_sync = ChainSync::new(params.config, &*params.chain); let chain_sync = ChainSync::new(params.config, &*params.chain);
let service = NetworkService::new(params.network_config.clone().into_basic()?)?; let service = NetworkService::new(params.network_config.clone().into_basic()?, connection_filter)?;
let sync = Arc::new(EthSync { let sync = Arc::new(EthSync {
network: service, network: service,
@ -736,7 +736,7 @@ impl LightSync {
(sync_handler, Arc::new(light_proto)) (sync_handler, Arc::new(light_proto))
}; };
let service = NetworkService::new(params.network_config)?; let service = NetworkService::new(params.network_config, None)?;
Ok(LightSync { Ok(LightSync {
proto: light_proto, proto: light_proto,

View File

@ -76,7 +76,7 @@ mod api;
pub use api::*; pub use api::*;
pub use chain::{SyncStatus, SyncState}; pub use chain::{SyncStatus, SyncState};
pub use network::{is_valid_node_url, NonReservedPeerMode, NetworkError}; pub use network::{is_valid_node_url, NonReservedPeerMode, NetworkError, ConnectionFilter, ConnectionDirection};
/// IPC interfaces /// IPC interfaces
#[cfg(feature="ipc")] #[cfg(feature="ipc")]

View File

@ -26,4 +26,4 @@ set -e
./scripts/validate_chainspecs.sh ./scripts/validate_chainspecs.sh
cargo test -j 8 $OPTIONS --features "$FEATURES" --all --exclude parity-ipfs-api --exclude evmjit $1 cargo test -j 8 $OPTIONS --features "$FEATURES" --all --exclude evmjit $1

View File

@ -13,6 +13,7 @@ rustc-hex = "1.0"
rand = "0.3.12" rand = "0.3.12"
libc = "0.2" libc = "0.2"
heapsize = { version = "0.4", optional = true } heapsize = { version = "0.4", optional = true }
plain_hasher = { path = "../plain_hasher" }
[features] [features]
x64asm_arithmetic=[] x64asm_arithmetic=[]

View File

@ -16,6 +16,7 @@ use std::collections::{HashMap, HashSet};
use rand::{Rand, Rng}; use rand::{Rand, Rng};
use rand::os::OsRng; use rand::os::OsRng;
use rustc_hex::{FromHex, FromHexError}; use rustc_hex::{FromHex, FromHexError};
use plain_hasher::PlainHasher;
use bigint::U256; use bigint::U256;
use libc::{c_void, memcmp}; use libc::{c_void, memcmp};
@ -446,41 +447,6 @@ impl_hash!(H2048, 256);
known_heap_size!(0, H32, H64, H128, H160, H256, H264, H512, H520, H1024, H2048); known_heap_size!(0, H32, H64, H128, H160, H256, H264, H512, H520, H1024, H2048);
// Specialized HashMap and HashSet // Specialized HashMap and HashSet
/// Hasher that just takes 8 bytes of the provided value.
/// May only be used for keys which are 32 bytes.
pub struct PlainHasher {
prefix: [u8; 8],
_marker: [u64; 0], // for alignment
}
impl Default for PlainHasher {
#[inline]
fn default() -> PlainHasher {
PlainHasher {
prefix: [0; 8],
_marker: [0; 0],
}
}
}
impl Hasher for PlainHasher {
#[inline]
fn finish(&self) -> u64 {
unsafe { ::std::mem::transmute(self.prefix) }
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
debug_assert!(bytes.len() == 32);
for quarter in bytes.chunks(8) {
for (x, y) in self.prefix.iter_mut().zip(quarter) {
*x ^= *y
}
}
}
}
/// Specialized version of `HashMap` with H256 keys and fast hashing function. /// Specialized version of `HashMap` with H256 keys and fast hashing function.
pub type H256FastMap<T> = HashMap<H256, T, BuildHasherDefault<PlainHasher>>; pub type H256FastMap<T> = HashMap<H256, T, BuildHasherDefault<PlainHasher>>;
/// Specialized version of `HashSet` with H256 keys and fast hashing function. /// Specialized version of `HashSet` with H256 keys and fast hashing function.

View File

@ -14,6 +14,7 @@ extern crate rand;
extern crate rustc_hex; extern crate rustc_hex;
extern crate bigint; extern crate bigint;
extern crate libc; extern crate libc;
extern crate plain_hasher;
#[cfg(feature="heapsizeof")] #[cfg(feature="heapsizeof")]
#[macro_use] #[macro_use]

View File

@ -37,9 +37,9 @@ struct BitVecJournal {
impl BitVecJournal { impl BitVecJournal {
pub fn new(size: usize) -> 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 { BitVecJournal {
elems: vec![0u64; size / 8 + extra], elems: vec![0u64; size / 64 + extra],
journal: HashSet::new(), journal: HashSet::new(),
} }
} }

View File

@ -0,0 +1,31 @@
// 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/>.
//! Connection filter trait.
use super::NodeId;
/// Filtered connection direction.
pub enum ConnectionDirection {
Inbound,
Outbound,
}
/// Connection filter. Each connection is checked against `connection_allowed`.
pub trait ConnectionFilter : Send + Sync {
/// Filter a connection. Returns `true` if connection should be allowed. `false` if rejected.
fn connection_allowed(&self, own_id: &NodeId, connecting_id: &NodeId, direction: ConnectionDirection) -> bool;
}

View File

@ -42,6 +42,7 @@ use discovery::{Discovery, TableUpdates, NodeEntry};
use ip_utils::{map_external_address, select_public_address}; use ip_utils::{map_external_address, select_public_address};
use path::restrict_permissions_owner; use path::restrict_permissions_owner;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use connection_filter::{ConnectionFilter, ConnectionDirection};
type Slab<T> = ::slab::Slab<T, usize>; type Slab<T> = ::slab::Slab<T, usize>;
@ -380,11 +381,12 @@ pub struct Host {
reserved_nodes: RwLock<HashSet<NodeId>>, reserved_nodes: RwLock<HashSet<NodeId>>,
num_sessions: AtomicUsize, num_sessions: AtomicUsize,
stopping: AtomicBool, stopping: AtomicBool,
filter: Option<Arc<ConnectionFilter>>,
} }
impl Host { impl Host {
/// Create a new instance /// Create a new instance
pub fn new(mut config: NetworkConfiguration, stats: Arc<NetworkStats>) -> Result<Host, NetworkError> { pub fn new(mut config: NetworkConfiguration, stats: Arc<NetworkStats>, filter: Option<Arc<ConnectionFilter>>) -> Result<Host, NetworkError> {
let mut listen_address = match config.listen_address { let mut listen_address = match config.listen_address {
None => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), DEFAULT_PORT)), None => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), DEFAULT_PORT)),
Some(addr) => addr, Some(addr) => addr,
@ -437,6 +439,7 @@ impl Host {
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),
filter: filter,
}; };
for n in boot_nodes { for n in boot_nodes {
@ -691,8 +694,12 @@ impl Host {
let max_handshakes_per_round = max_handshakes / 2; let max_handshakes_per_round = max_handshakes / 2;
let mut started: usize = 0; let mut started: usize = 0;
for id in nodes.filter(|id| !self.have_session(id) && !self.connecting_to(id) && *id != self_id) for id in nodes.filter(|id|
.take(min(max_handshakes_per_round, max_handshakes - handshake_count)) { !self.have_session(id) &&
!self.connecting_to(id) &&
*id != self_id &&
self.filter.as_ref().map_or(true, |f| f.connection_allowed(&self_id, &id, ConnectionDirection::Outbound))
).take(min(max_handshakes_per_round, max_handshakes - handshake_count)) {
self.connect_peer(&id, io); self.connect_peer(&id, io);
started += 1; started += 1;
} }
@ -827,7 +834,7 @@ impl Host {
Ok(SessionData::Ready) => { Ok(SessionData::Ready) => {
self.num_sessions.fetch_add(1, AtomicOrdering::SeqCst); self.num_sessions.fetch_add(1, AtomicOrdering::SeqCst);
let session_count = self.session_count(); let session_count = self.session_count();
let (min_peers, max_peers, reserved_only) = { let (min_peers, max_peers, reserved_only, self_id) = {
let info = self.info.read(); let info = self.info.read();
let mut max_peers = info.config.max_peers; let mut max_peers = info.config.max_peers;
for cap in s.info.capabilities.iter() { for cap in s.info.capabilities.iter() {
@ -836,7 +843,7 @@ impl Host {
break; break;
} }
} }
(info.config.min_peers as usize, max_peers as usize, info.config.non_reserved_mode == NonReservedPeerMode::Deny) (info.config.min_peers as usize, max_peers as usize, info.config.non_reserved_mode == NonReservedPeerMode::Deny, info.id().clone())
}; };
let id = s.id().expect("Ready session always has id").clone(); let id = s.id().expect("Ready session always has id").clone();
@ -852,6 +859,14 @@ impl Host {
break; break;
} }
} }
if !self.filter.as_ref().map_or(true, |f| f.connection_allowed(&self_id, &id, ConnectionDirection::Inbound)) {
trace!(target: "network", "Inbound connection not allowed for {:?}", id);
s.disconnect(io, DisconnectReason::UnexpectedIdentity);
kill = true;
break;
}
ready_id = Some(id); ready_id = Some(id);
// Add it to the node table // Add it to the node table
@ -1266,7 +1281,7 @@ fn host_client_url() {
let mut config = NetworkConfiguration::new_local(); let mut config = NetworkConfiguration::new_local();
let key = "6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2".parse().unwrap(); let key = "6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2".parse().unwrap();
config.use_secret = Some(key); config.use_secret = Some(key);
let host: Host = Host::new(config, Arc::new(NetworkStats::new())).unwrap(); let host: Host = Host::new(config, Arc::new(NetworkStats::new()), None).unwrap();
assert!(host.local_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@")); assert!(host.local_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@"));
} }

View File

@ -44,7 +44,7 @@
//! } //! }
//! //!
//! fn main () { //! fn main () {
//! let mut service = NetworkService::new(NetworkConfiguration::new_local()).expect("Error creating network service"); //! let mut service = NetworkService::new(NetworkConfiguration::new_local(), None).expect("Error creating network service");
//! service.start().expect("Error starting service"); //! service.start().expect("Error starting service");
//! service.register_protocol(Arc::new(MyHandler), *b"myp", 1, &[1u8]); //! service.register_protocol(Arc::new(MyHandler), *b"myp", 1, &[1u8]);
//! //!
@ -95,6 +95,7 @@ mod error;
mod node_table; mod node_table;
mod stats; mod stats;
mod ip_utils; mod ip_utils;
mod connection_filter;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -104,6 +105,7 @@ pub use service::NetworkService;
pub use error::NetworkError; pub use error::NetworkError;
pub use stats::NetworkStats; pub use stats::NetworkStats;
pub use session::SessionInfo; pub use session::SessionInfo;
pub use connection_filter::{ConnectionFilter, ConnectionDirection};
pub use io::TimerToken; pub use io::TimerToken;
pub use node_table::{is_valid_node_url, NodeId}; pub use node_table::{is_valid_node_url, NodeId};

View File

@ -22,6 +22,7 @@ use io::*;
use parking_lot::RwLock; use parking_lot::RwLock;
use std::sync::Arc; use std::sync::Arc;
use ansi_term::Colour; use ansi_term::Colour;
use connection_filter::ConnectionFilter;
struct HostHandler { struct HostHandler {
public_url: RwLock<Option<String>> public_url: RwLock<Option<String>>
@ -48,11 +49,12 @@ pub struct NetworkService {
stats: Arc<NetworkStats>, stats: Arc<NetworkStats>,
host_handler: Arc<HostHandler>, host_handler: Arc<HostHandler>,
config: NetworkConfiguration, config: NetworkConfiguration,
filter: Option<Arc<ConnectionFilter>>,
} }
impl NetworkService { impl NetworkService {
/// Starts IO event loop /// Starts IO event loop
pub fn new(config: NetworkConfiguration) -> Result<NetworkService, NetworkError> { pub fn new(config: NetworkConfiguration, filter: Option<Arc<ConnectionFilter>>) -> Result<NetworkService, NetworkError> {
let host_handler = Arc::new(HostHandler { public_url: RwLock::new(None) }); let host_handler = Arc::new(HostHandler { public_url: RwLock::new(None) });
let io_service = IoService::<NetworkIoMessage>::start()?; let io_service = IoService::<NetworkIoMessage>::start()?;
@ -65,6 +67,7 @@ impl NetworkService {
host: RwLock::new(None), host: RwLock::new(None),
config: config, config: config,
host_handler: host_handler, host_handler: host_handler,
filter: filter,
}) })
} }
@ -115,7 +118,7 @@ impl NetworkService {
pub fn start(&self) -> Result<(), NetworkError> { pub fn start(&self) -> Result<(), NetworkError> {
let mut host = self.host.write(); let mut host = self.host.write();
if host.is_none() { if host.is_none() {
let h = Arc::new(Host::new(self.config.clone(), self.stats.clone())?); let h = Arc::new(Host::new(self.config.clone(), self.stats.clone(), self.filter.clone())?);
self.io_service.register_handler(h.clone())?; self.io_service.register_handler(h.clone())?;
*host = Some(h); *host = Some(h);
} }

View File

@ -92,7 +92,7 @@ impl NetworkProtocolHandler for TestProtocol {
#[test] #[test]
fn net_service() { fn net_service() {
let service = NetworkService::new(NetworkConfiguration::new_local()).expect("Error creating network service"); let service = NetworkService::new(NetworkConfiguration::new_local(), None).expect("Error creating network service");
service.start().unwrap(); service.start().unwrap();
service.register_protocol(Arc::new(TestProtocol::new(false)), *b"myp", 1, &[1u8]).unwrap(); service.register_protocol(Arc::new(TestProtocol::new(false)), *b"myp", 1, &[1u8]).unwrap();
} }
@ -104,13 +104,13 @@ fn net_connect() {
let mut config1 = NetworkConfiguration::new_local(); let mut config1 = NetworkConfiguration::new_local();
config1.use_secret = Some(key1.secret().clone()); config1.use_secret = Some(key1.secret().clone());
config1.boot_nodes = vec![ ]; config1.boot_nodes = vec![ ];
let mut service1 = NetworkService::new(config1).unwrap(); let mut service1 = NetworkService::new(config1, None).unwrap();
service1.start().unwrap(); service1.start().unwrap();
let handler1 = TestProtocol::register(&mut service1, false); let handler1 = TestProtocol::register(&mut service1, false);
let mut config2 = NetworkConfiguration::new_local(); let mut config2 = NetworkConfiguration::new_local();
info!("net_connect: local URL: {}", service1.local_url().unwrap()); info!("net_connect: local URL: {}", service1.local_url().unwrap());
config2.boot_nodes = vec![ service1.local_url().unwrap() ]; config2.boot_nodes = vec![ service1.local_url().unwrap() ];
let mut service2 = NetworkService::new(config2).unwrap(); let mut service2 = NetworkService::new(config2, None).unwrap();
service2.start().unwrap(); service2.start().unwrap();
let handler2 = TestProtocol::register(&mut service2, false); let handler2 = TestProtocol::register(&mut service2, false);
while !handler1.got_packet() && !handler2.got_packet() && (service1.stats().sessions() == 0 || service2.stats().sessions() == 0) { while !handler1.got_packet() && !handler2.got_packet() && (service1.stats().sessions() == 0 || service2.stats().sessions() == 0) {
@ -123,7 +123,7 @@ fn net_connect() {
#[test] #[test]
fn net_start_stop() { fn net_start_stop() {
let config = NetworkConfiguration::new_local(); let config = NetworkConfiguration::new_local();
let service = NetworkService::new(config).unwrap(); let service = NetworkService::new(config, None).unwrap();
service.start().unwrap(); service.start().unwrap();
service.stop().unwrap(); service.stop().unwrap();
service.start().unwrap(); service.start().unwrap();
@ -135,12 +135,12 @@ fn net_disconnect() {
let mut config1 = NetworkConfiguration::new_local(); let mut config1 = NetworkConfiguration::new_local();
config1.use_secret = Some(key1.secret().clone()); config1.use_secret = Some(key1.secret().clone());
config1.boot_nodes = vec![ ]; config1.boot_nodes = vec![ ];
let mut service1 = NetworkService::new(config1).unwrap(); let mut service1 = NetworkService::new(config1, None).unwrap();
service1.start().unwrap(); service1.start().unwrap();
let handler1 = TestProtocol::register(&mut service1, false); let handler1 = TestProtocol::register(&mut service1, false);
let mut config2 = NetworkConfiguration::new_local(); let mut config2 = NetworkConfiguration::new_local();
config2.boot_nodes = vec![ service1.local_url().unwrap() ]; config2.boot_nodes = vec![ service1.local_url().unwrap() ];
let mut service2 = NetworkService::new(config2).unwrap(); let mut service2 = NetworkService::new(config2, None).unwrap();
service2.start().unwrap(); service2.start().unwrap();
let handler2 = TestProtocol::register(&mut service2, true); let handler2 = TestProtocol::register(&mut service2, true);
while !(handler1.got_disconnect() && handler2.got_disconnect()) { while !(handler1.got_disconnect() && handler2.got_disconnect()) {
@ -153,7 +153,7 @@ fn net_disconnect() {
#[test] #[test]
fn net_timeout() { fn net_timeout() {
let config = NetworkConfiguration::new_local(); let config = NetworkConfiguration::new_local();
let mut service = NetworkService::new(config).unwrap(); let mut service = NetworkService::new(config, None).unwrap();
service.start().unwrap(); service.start().unwrap();
let handler = TestProtocol::register(&mut service, false); let handler = TestProtocol::register(&mut service, false);
while !handler.got_timeout() { while !handler.got_timeout() {

View File

@ -0,0 +1,12 @@
[package]
name = "plain_hasher"
description = "Hasher for 32-bit keys."
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
license = "MIT"
keywords = ["hash", "hasher"]
categories = ["no-std"]
homepage = "https://github.com/paritytech/plain_hasher"
[dependencies]
crunchy = "0.1.6"

View File

@ -0,0 +1,33 @@
#![feature(test)]
extern crate test;
extern crate plain_hasher;
use std::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
use test::{Bencher, black_box};
use plain_hasher::PlainHasher;
#[bench]
fn write_plain_hasher(b: &mut Bencher) {
b.iter(|| {
let n: u8 = black_box(100);
(0..n).fold(PlainHasher::default(), |mut old, new| {
let bb = black_box([new; 32]);
old.write(&bb as &[u8]);
old
});
});
}
#[bench]
fn write_default_hasher(b: &mut Bencher) {
b.iter(|| {
let n: u8 = black_box(100);
(0..n).fold(DefaultHasher::default(), |mut old, new| {
let bb = black_box([new; 32]);
old.write(&bb as &[u8]);
old
});
});
}

View File

@ -0,0 +1,55 @@
#![no_std]
#[macro_use]
extern crate crunchy;
use core::{hash, mem};
/// Hasher that just takes 8 bytes of the provided value.
/// May only be used for keys which are 32 bytes.
#[derive(Default)]
pub struct PlainHasher {
prefix: u64,
}
impl hash::Hasher for PlainHasher {
#[inline]
fn finish(&self) -> u64 {
self.prefix
}
#[inline]
#[allow(unused_assignments)]
fn write(&mut self, bytes: &[u8]) {
debug_assert!(bytes.len() == 32);
unsafe {
let mut bytes_ptr = bytes.as_ptr();
let prefix_u8: &mut [u8; 8] = mem::transmute(&mut self.prefix);
let mut prefix_ptr = prefix_u8.as_mut_ptr();
unroll! {
for _i in 0..8 {
*prefix_ptr ^= (*bytes_ptr ^ *bytes_ptr.offset(8)) ^ (*bytes_ptr.offset(16) ^ *bytes_ptr.offset(24));
bytes_ptr = bytes_ptr.offset(1);
prefix_ptr = prefix_ptr.offset(1);
}
}
}
}
}
#[cfg(test)]
mod tests {
use core::hash::Hasher;
use super::PlainHasher;
#[test]
fn it_works() {
let mut bytes = [32u8; 32];
bytes[0] = 15;
let mut hasher = PlainHasher::default();
hasher.write(&bytes);
assert_eq!(hasher.prefix, 47);
}
}

View File

@ -20,7 +20,7 @@
extern crate futures; extern crate futures;
extern crate tokio_core; extern crate tokio_core;
use std::thread; use std::{fmt, thread};
use std::sync::mpsc; use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use futures::{Future, IntoFuture}; use futures::{Future, IntoFuture};
@ -81,7 +81,19 @@ enum Mode {
ThreadPerFuture, 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 { pub struct Remote {
inner: Mode, inner: Mode,
} }

View File

@ -17,6 +17,7 @@
//! Disk-backed `HashDB` implementation. //! Disk-backed `HashDB` implementation.
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc; use std::sync::Arc;
use rlp::*; use rlp::*;
use hashdb::*; use hashdb::*;
@ -66,23 +67,28 @@ impl ArchiveDB {
impl HashDB for ArchiveDB { impl HashDB for ArchiveDB {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = HashMap::new(); let mut ret: HashMap<H256, i32> = self.backing.iter(self.column)
for (key, _) in self.backing.iter(self.column) { .map(|(key, _)| (H256::from_slice(&*key), 1))
let h = H256::from_slice(&*key); .collect();
ret.insert(h, 1);
}
for (key, refs) in self.overlay.keys() { for (key, refs) in self.overlay.keys() {
let refs = *ret.get(&key).unwrap_or(&0) + refs; match ret.entry(key) {
ret.insert(key, refs); Entry::Occupied(mut entry) => {
*entry.get_mut() += refs;
},
Entry::Vacant(entry) => {
entry.insert(refs);
}
}
} }
ret ret
} }
fn get(&self, key: &H256) -> Option<DBValue> { fn get(&self, key: &H256) -> Option<DBValue> {
let k = self.overlay.raw(key); if let Some((d, rc)) = self.overlay.raw(key) {
if let Some((d, rc)) = k { if rc > 0 {
if rc > 0 { return Some(d); } return Some(d);
}
} }
self.payload(key) self.payload(key)
} }

View File

@ -18,6 +18,7 @@
use std::fmt; use std::fmt;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc; use std::sync::Arc;
use parking_lot::RwLock; use parking_lot::RwLock;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
@ -159,33 +160,38 @@ impl EarlyMergeDB {
fn insert_keys(inserts: &[(H256, DBValue)], backing: &KeyValueDB, col: Option<u32>, refs: &mut HashMap<H256, RefInfo>, batch: &mut DBTransaction, trace: bool) { fn insert_keys(inserts: &[(H256, DBValue)], backing: &KeyValueDB, col: Option<u32>, refs: &mut HashMap<H256, RefInfo>, batch: &mut DBTransaction, trace: bool) {
for &(ref h, ref d) in inserts { for &(ref h, ref d) in inserts {
if let Some(c) = refs.get_mut(h) { match refs.entry(*h) {
// already counting. increment. Entry::Occupied(mut entry) => {
c.queue_refs += 1; let info = entry.get_mut();
if trace { // already counting. increment.
trace!(target: "jdb.fine", " insert({}): In queue: Incrementing refs to {}", h, c.queue_refs); info.queue_refs += 1;
} if trace {
continue; trace!(target: "jdb.fine", " insert({}): In queue: Incrementing refs to {}", h, info.queue_refs);
} }
},
// this is the first entry for this node in the journal. Entry::Vacant(entry) => {
if backing.get(col, h).expect("Low-level database error. Some issue with your hard disk?").is_some() { // this is the first entry for this node in the journal.
// already in the backing DB. start counting, and remember it was already in. let in_archive = backing.get(col, h).expect("Low-level database error. Some issue with your hard disk?").is_some();
Self::set_already_in(batch, col, h); if in_archive {
refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: true}); // already in the backing DB. start counting, and remember it was already in.
if trace { Self::set_already_in(batch, col, h);
trace!(target: "jdb.fine", " insert({}): New to queue, in DB: Recording and inserting into queue", h); if trace {
} trace!(target: "jdb.fine", " insert({}): New to queue, in DB: Recording and inserting into queue", h);
continue; }
} } else {
// Gets removed when a key leaves the journal, so should never be set when we're placing a new key.
// Gets removed when a key leaves the journal, so should never be set when we're placing a new key. //Self::reset_already_in(&h);
//Self::reset_already_in(&h); assert!(!Self::is_already_in(backing, col, h));
assert!(!Self::is_already_in(backing, col, &h)); if trace {
batch.put(col, h, d); trace!(target: "jdb.fine", " insert({}): New to queue, not in DB: Inserting into queue and DB", h);
refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: false}); }
if trace { batch.put(col, h, d);
trace!(target: "jdb.fine", " insert({}): New to queue, not in DB: Inserting into queue and DB", h); }
entry.insert(RefInfo {
queue_refs: 1,
in_archive: in_archive,
});
},
} }
} }
} }
@ -193,15 +199,20 @@ impl EarlyMergeDB {
fn replay_keys(inserts: &[H256], backing: &KeyValueDB, col: Option<u32>, refs: &mut HashMap<H256, RefInfo>) { fn replay_keys(inserts: &[H256], backing: &KeyValueDB, col: Option<u32>, refs: &mut HashMap<H256, RefInfo>) {
trace!(target: "jdb.fine", "replay_keys: inserts={:?}, refs={:?}", inserts, refs); trace!(target: "jdb.fine", "replay_keys: inserts={:?}, refs={:?}", inserts, refs);
for h in inserts { for h in inserts {
if let Some(c) = refs.get_mut(h) { match refs.entry(*h) {
// already counting. increment. // already counting. increment.
c.queue_refs += 1; Entry::Occupied(mut entry) => {
continue; entry.get_mut().queue_refs += 1;
},
// this is the first entry for this node in the journal.
// it is initialised to 1 if it was already in.
Entry::Vacant(entry) => {
entry.insert(RefInfo {
queue_refs: 1,
in_archive: Self::is_already_in(backing, col, h),
});
},
} }
// this is the first entry for this node in the journal.
// it is initialised to 1 if it was already in.
refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: Self::is_already_in(backing, col, h)});
} }
trace!(target: "jdb.fine", "replay_keys: (end) refs={:?}", refs); trace!(target: "jdb.fine", "replay_keys: (end) refs={:?}", refs);
} }
@ -213,50 +224,54 @@ impl EarlyMergeDB {
// (the latter option would then mean removing the RefInfo, since it would no longer be counted in the queue.) // (the latter option would then mean removing the RefInfo, since it would no longer be counted in the queue.)
// both are valid, but we switch between them depending on context. // both are valid, but we switch between them depending on context.
// All inserts in queue (i.e. those which may yet be reverted) have an entry in refs. // All inserts in queue (i.e. those which may yet be reverted) have an entry in refs.
for h in deletes.iter() { for h in deletes {
let mut n: Option<RefInfo> = None; match refs.entry(*h) {
if let Some(c) = refs.get_mut(h) { Entry::Occupied(mut entry) => {
if c.in_archive && from == RemoveFrom::Archive { if entry.get().in_archive && from == RemoveFrom::Archive {
c.in_archive = false; entry.get_mut().in_archive = false;
Self::reset_already_in(batch, col, h); Self::reset_already_in(batch, col, h);
if trace { if trace {
trace!(target: "jdb.fine", " remove({}): In archive, 1 in queue: Reducing to queue only and recording", h); trace!(target: "jdb.fine", " remove({}): In archive, 1 in queue: Reducing to queue only and recording", h);
}
continue;
} }
continue; if entry.get().queue_refs > 1 {
} else if c.queue_refs > 1 { entry.get_mut().queue_refs -= 1;
c.queue_refs -= 1; if trace {
if trace { trace!(target: "jdb.fine", " remove({}): In queue > 1 refs: Decrementing ref count to {}", h, entry.get().queue_refs);
trace!(target: "jdb.fine", " remove({}): In queue > 1 refs: Decrementing ref count to {}", h, c.queue_refs); }
continue;
} }
continue;
} else { let queue_refs = entry.get().queue_refs;
n = Some(c.clone()); let in_archive = entry.get().in_archive;
}
} match (queue_refs, in_archive) {
match n { (1, true) => {
Some(RefInfo{queue_refs: 1, in_archive: true}) => { entry.remove();
refs.remove(h); Self::reset_already_in(batch, col, h);
Self::reset_already_in(batch, col, h); if trace {
if trace { trace!(target: "jdb.fine", " remove({}): In archive, 1 in queue: Removing from queue and leaving in archive", h);
trace!(target: "jdb.fine", " remove({}): In archive, 1 in queue: Removing from queue and leaving in archive", h); }
},
(1, false) => {
entry.remove();
batch.delete(col, h);
if trace {
trace!(target: "jdb.fine", " remove({}): Not in archive, only 1 ref in queue: Removing from queue and DB", h);
}
},
_ => panic!("Invalid value in refs: {:?}", entry.get()),
} }
} },
Some(RefInfo{queue_refs: 1, in_archive: false}) => { Entry::Vacant(_entry) => {
refs.remove(h);
batch.delete(col, h);
if trace {
trace!(target: "jdb.fine", " remove({}): Not in archive, only 1 ref in queue: Removing from queue and DB", h);
}
}
None => {
// Gets removed when moving from 1 to 0 additional refs. Should never be here at 0 additional refs. // Gets removed when moving from 1 to 0 additional refs. Should never be here at 0 additional refs.
//assert!(!Self::is_already_in(db, &h)); //assert!(!Self::is_already_in(db, &h));
batch.delete(col, h); batch.delete(col, h);
if trace { if trace {
trace!(target: "jdb.fine", " remove({}): Not in queue - MUST BE IN ARCHIVE: Removing from DB", h); trace!(target: "jdb.fine", " remove({}): Not in queue - MUST BE IN ARCHIVE: Removing from DB", h);
} }
} },
_ => panic!("Invalid value in refs: {:?}", n),
} }
} }
} }
@ -311,23 +326,28 @@ impl EarlyMergeDB {
impl HashDB for EarlyMergeDB { impl HashDB for EarlyMergeDB {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = HashMap::new(); let mut ret: HashMap<H256, i32> = self.backing.iter(self.column)
for (key, _) in self.backing.iter(self.column) { .map(|(key, _)| (H256::from_slice(&*key), 1))
let h = H256::from_slice(&*key); .collect();
ret.insert(h, 1);
}
for (key, refs) in self.overlay.keys() { for (key, refs) in self.overlay.keys() {
let refs = *ret.get(&key).unwrap_or(&0) + refs; match ret.entry(key) {
ret.insert(key, refs); Entry::Occupied(mut entry) => {
*entry.get_mut() += refs;
},
Entry::Vacant(entry) => {
entry.insert(refs);
}
}
} }
ret ret
} }
fn get(&self, key: &H256) -> Option<DBValue> { fn get(&self, key: &H256) -> Option<DBValue> {
let k = self.overlay.raw(key); if let Some((d, rc)) = self.overlay.raw(key) {
if let Some((d, rc)) = k { if rc > 0 {
if rc > 0 { return Some(d) } return Some(d)
}
} }
self.payload(key) self.payload(key)
} }

View File

@ -17,6 +17,7 @@
//! `JournalDB` over in-memory overlay //! `JournalDB` over in-memory overlay
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc; use std::sync::Arc;
use parking_lot::RwLock; use parking_lot::RwLock;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
@ -407,23 +408,28 @@ impl JournalDB for OverlayRecentDB {
impl HashDB for OverlayRecentDB { impl HashDB for OverlayRecentDB {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = HashMap::new(); let mut ret: HashMap<H256, i32> = self.backing.iter(self.column)
for (key, _) in self.backing.iter(self.column) { .map(|(key, _)| (H256::from_slice(&*key), 1))
let h = H256::from_slice(&*key); .collect();
ret.insert(h, 1);
}
for (key, refs) in self.transaction_overlay.keys() { for (key, refs) in self.transaction_overlay.keys() {
let refs = *ret.get(&key).unwrap_or(&0) + refs; match ret.entry(key) {
ret.insert(key, refs); Entry::Occupied(mut entry) => {
*entry.get_mut() += refs;
},
Entry::Vacant(entry) => {
entry.insert(refs);
}
}
} }
ret ret
} }
fn get(&self, key: &H256) -> Option<DBValue> { fn get(&self, key: &H256) -> Option<DBValue> {
let k = self.transaction_overlay.raw(key); if let Some((d, rc)) = self.transaction_overlay.raw(key) {
if let Some((d, rc)) = k { if rc > 0 {
if rc > 0 { return Some(d) } return Some(d)
}
} }
let v = { let v = {
let journal_overlay = self.journal_overlay.read(); let journal_overlay = self.journal_overlay.read();

View File

@ -198,7 +198,7 @@ impl JournalDB for RefCountedDB {
fn consolidate(&mut self, mut with: MemoryDB) { fn consolidate(&mut self, mut with: MemoryDB) {
for (key, (value, rc)) in with.drain() { for (key, (value, rc)) in with.drain() {
for _ in 0..rc { for _ in 0..rc {
self.emplace(key.clone(), value.clone()); self.emplace(key, value.clone());
} }
for _ in rc..0 { for _ in rc..0 {

View File

@ -16,14 +16,14 @@
//! Reference-counted memory-based `HashDB` implementation. //! Reference-counted memory-based `HashDB` implementation.
use hash::*;
use rlp::*;
use sha3::*;
use hashdb::*;
use heapsize::*;
use std::mem; use std::mem;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use heapsize::HeapSizeOf;
use hash::{H256FastMap, H256};
use rlp::NULL_RLP;
use sha3::*;
use hashdb::*;
/// Reference-counted memory-based `HashDB` implementation. /// Reference-counted memory-based `HashDB` implementation.
/// ///
@ -181,7 +181,13 @@ impl HashDB for MemoryDB {
} }
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
self.data.iter().filter_map(|(k, v)| if v.1 != 0 {Some((k.clone(), v.1))} else {None}).collect() self.data.iter()
.filter_map(|(k, v)| if v.1 != 0 {
Some((*k, v.1))
} else {
None
})
.collect()
} }
fn contains(&self, key: &H256) -> bool { fn contains(&self, key: &H256) -> bool {
@ -200,16 +206,17 @@ impl HashDB for MemoryDB {
return SHA3_NULL_RLP.clone(); return SHA3_NULL_RLP.clone();
} }
let key = value.sha3(); let key = value.sha3();
if match self.data.get_mut(&key) { match self.data.entry(key) {
Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => { Entry::Occupied(mut entry) => {
*old_value = DBValue::from_slice(value); let &mut (ref mut old_value, ref mut rc) = entry.get_mut();
if *rc >= -0x80000000i32 && *rc <= 0 {
*old_value = DBValue::from_slice(value);
}
*rc += 1; *rc += 1;
false
}, },
Some(&mut (_, ref mut x)) => { *x += 1; false } , Entry::Vacant(entry) => {
None => true, entry.insert((DBValue::from_slice(value), 1));
}{ // ... None falls through into... },
self.data.insert(key.clone(), (DBValue::from_slice(value), 1));
} }
key key
} }
@ -219,17 +226,18 @@ impl HashDB for MemoryDB {
return; return;
} }
match self.data.get_mut(&key) { match self.data.entry(key) {
Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => { Entry::Occupied(mut entry) => {
*old_value = value; let &mut (ref mut old_value, ref mut rc) = entry.get_mut();
if *rc >= -0x80000000i32 && *rc <= 0 {
*old_value = value;
}
*rc += 1; *rc += 1;
return;
}, },
Some(&mut (_, ref mut x)) => { *x += 1; return; } , Entry::Vacant(entry) => {
None => {}, entry.insert((value, 1));
},
} }
// ... None falls through into...
self.data.insert(key, (value, 1));
} }
fn remove(&mut self, key: &H256) { fn remove(&mut self, key: &H256) {
@ -237,11 +245,14 @@ impl HashDB for MemoryDB {
return; return;
} }
if match self.data.get_mut(key) { match self.data.entry(*key) {
Some(&mut (_, ref mut x)) => { *x -= 1; false } Entry::Occupied(mut entry) => {
None => true let &mut (_, ref mut rc) = entry.get_mut();
}{ // ... None falls through into... *rc -= 1;
self.data.insert(key.clone(), (DBValue::new(), -1)); },
Entry::Vacant(entry) => {
entry.insert((DBValue::new(), -1));
},
} }
} }
} }

View File

@ -16,13 +16,14 @@
//! Disk-backed `HashDB` implementation. //! Disk-backed `HashDB` implementation.
use std::sync::Arc;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use error::*; use error::*;
use hash::*; use hash::*;
use rlp::*; use rlp::*;
use hashdb::*; use hashdb::*;
use memorydb::*; use memorydb::*;
use std::sync::*;
use std::collections::HashMap;
use kvdb::{KeyValueDB, DBTransaction}; use kvdb::{KeyValueDB, DBTransaction};
/// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay. /// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay.
@ -125,19 +126,27 @@ impl OverlayDB {
impl HashDB for OverlayDB { impl HashDB for OverlayDB {
fn keys(&self) -> HashMap<H256, i32> { fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = HashMap::new(); let mut ret: HashMap<H256, i32> = self.backing.iter(self.column)
for (key, _) in self.backing.iter(self.column) { .map(|(key, _)| {
let h = H256::from_slice(&*key); let h = H256::from_slice(&*key);
let r = self.payload(&h).unwrap().1; let r = self.payload(&h).unwrap().1;
ret.insert(h, r as i32); (h, r as i32)
} })
.collect();
for (key, refs) in self.overlay.keys() { for (key, refs) in self.overlay.keys() {
let refs = *ret.get(&key).unwrap_or(&0) + refs; match ret.entry(key) {
ret.insert(key, refs); Entry::Occupied(mut entry) => {
*entry.get_mut() += refs;
},
Entry::Vacant(entry) => {
entry.insert(refs);
}
}
} }
ret ret
} }
fn get(&self, key: &H256) -> Option<DBValue> { fn get(&self, key: &H256) -> Option<DBValue> {
// return ok if positive; if negative, check backing - might be enough references there to make // return ok if positive; if negative, check backing - might be enough references there to make
// it positive again. // it positive again.
@ -165,6 +174,7 @@ impl HashDB for OverlayDB {
_ => None, _ => None,
} }
} }
fn contains(&self, key: &H256) -> bool { fn contains(&self, key: &H256) -> bool {
// return ok if positive; if negative, check backing - might be enough references there to make // return ok if positive; if negative, check backing - might be enough references there to make
// it positive again. // it positive again.
@ -185,6 +195,7 @@ impl HashDB for OverlayDB {
} }
} }
} }
fn insert(&mut self, value: &[u8]) -> H256 { self.overlay.insert(value) } fn insert(&mut self, value: &[u8]) -> H256 { self.overlay.insert(value) }
fn emplace(&mut self, key: H256, value: DBValue) { self.overlay.emplace(key, value); } fn emplace(&mut self, key: H256, value: DBValue) { self.overlay.emplace(key, value); }
fn remove(&mut self, key: &H256) { self.overlay.remove(key); } fn remove(&mut self, key: &H256) { self.overlay.remove(key); }

View File

@ -217,59 +217,66 @@ impl<'a> TrieDBIterator<'a> {
Ok(r) Ok(r)
} }
fn seek_descend<'key>(&mut self, node_data: DBValue, key: &NibbleSlice<'key>) -> super::Result<()> { fn seek<'key>(&mut self, mut node_data: DBValue, mut key: NibbleSlice<'key>) -> super::Result<()> {
let node = Node::decoded(&node_data); loop {
match node { let (data, mid) = {
Node::Leaf(ref slice, _) => { let node = Node::decoded(&node_data);
if slice == key { match node {
self.trail.push(Crumb { Node::Leaf(slice, _) => {
status: Status::At, if slice == key {
node: node.clone().into(), self.trail.push(Crumb {
}); status: Status::At,
} else { node: node.clone().into(),
self.trail.push(Crumb { });
status: Status::Exiting, } else {
node: node.clone().into(), self.trail.push(Crumb {
}); status: Status::Exiting,
} node: node.clone().into(),
});
}
self.key_nibbles.extend(slice.iter()); self.key_nibbles.extend(slice.iter());
Ok(()) return Ok(())
}, },
Node::Extension(ref slice, ref item) => { Node::Extension(ref slice, ref item) => {
if key.starts_with(slice) { if key.starts_with(slice) {
self.trail.push(Crumb { self.trail.push(Crumb {
status: Status::At, status: Status::At,
node: node.clone().into(), node: node.clone().into(),
}); });
self.key_nibbles.extend(slice.iter()); self.key_nibbles.extend(slice.iter());
let data = self.db.get_raw_or_lookup(&*item)?; let data = self.db.get_raw_or_lookup(&*item)?;
self.seek_descend(data, &key.mid(slice.len())) (data, slice.len())
} else { } else {
self.descend(&node_data)?; self.descend(&node_data)?;
Ok(()) return Ok(())
}
},
Node::Branch(ref nodes, _) => match key.is_empty() {
true => {
self.trail.push(Crumb {
status: Status::At,
node: node.clone().into(),
});
return Ok(())
},
false => {
let i = key.at(0);
self.trail.push(Crumb {
status: Status::AtChild(i as usize),
node: node.clone().into(),
});
self.key_nibbles.push(i);
let child = self.db.get_raw_or_lookup(&*nodes[i as usize])?;
(child, 1)
}
},
_ => return Ok(()),
} }
}, };
Node::Branch(ref nodes, _) => match key.is_empty() {
true => { node_data = data;
self.trail.push(Crumb { key = key.mid(mid);
status: Status::At,
node: node.clone().into(),
});
Ok(())
},
false => {
let i = key.at(0);
self.trail.push(Crumb {
status: Status::AtChild(i as usize),
node: node.clone().into(),
});
self.key_nibbles.push(i);
let child = self.db.get_raw_or_lookup(&*nodes[i as usize])?;
self.seek_descend(child, &key.mid(1))
}
},
_ => Ok(())
} }
} }
@ -314,7 +321,7 @@ impl<'a> TrieIterator for TrieDBIterator<'a> {
self.trail.clear(); self.trail.clear();
self.key_nibbles.clear(); self.key_nibbles.clear();
let root_rlp = self.db.root_data()?; let root_rlp = self.db.root_data()?;
self.seek_descend(root_rlp, &NibbleSlice::new(key)) self.seek(root_rlp, NibbleSlice::new(key))
} }
} }

View File

@ -115,7 +115,7 @@ pub fn sec_trie_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
let gen_input = input let gen_input = input
// first put elements into btree to sort them and to remove duplicates // first put elements into btree to sort them and to remove duplicates
.into_iter() .into_iter()
.map(|(k, v)| (k.sha3().to_vec(), v)) .map(|(k, v)| (k.sha3(), v))
.collect::<BTreeMap<_, _>>() .collect::<BTreeMap<_, _>>()
// then move them to a vector // then move them to a vector
.into_iter() .into_iter()
@ -155,8 +155,7 @@ fn hex_prefix_encode(nibbles: &[u8], leaf: bool) -> Vec<u8> {
let oddness_factor = inlen % 2; let oddness_factor = inlen % 2;
// next even number divided by two // next even number divided by two
let reslen = (inlen + 2) >> 1; let reslen = (inlen + 2) >> 1;
let mut res = vec![]; let mut res = Vec::with_capacity(reslen);
res.reserve(reslen);
let first_byte = { let first_byte = {
let mut bits = ((inlen as u8 & 1) + (2 * leaf as u8)) << 4; let mut bits = ((inlen as u8 & 1) + (2 * leaf as u8)) << 4;
@ -180,11 +179,11 @@ fn hex_prefix_encode(nibbles: &[u8], leaf: bool) -> Vec<u8> {
/// Converts slice of bytes to nibbles. /// Converts slice of bytes to nibbles.
fn as_nibbles(bytes: &[u8]) -> Vec<u8> { fn as_nibbles(bytes: &[u8]) -> Vec<u8> {
let mut res = vec![]; let mut res = Vec::with_capacity(bytes.len() * 2);
res.reserve(bytes.len() * 2);
for i in 0..bytes.len() { for i in 0..bytes.len() {
res.push(bytes[i] >> 4); let byte = bytes[i];
res.push((bytes[i] << 4) >> 4); res.push(byte >> 4);
res.push(byte & 0b1111);
} }
res res
} }