Merge branch 'master' into light-poa
This commit is contained in:
commit
d3c3e0b164
45
Cargo.lock
generated
45
Cargo.lock
generated
@ -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"
|
||||||
|
@ -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"]
|
||||||
|
@ -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"]
|
||||||
|
|
||||||
|
18
dapps/node-health/Cargo.toml
Normal file
18
dapps/node-health/Cargo.toml
Normal 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" }
|
122
dapps/node-health/src/health.rs
Normal file
122
dapps/node-health/src/health.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
49
dapps/node-health/src/lib.rs
Normal file
49
dapps/node-health/src/lib.rs
Normal 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);
|
||||||
|
}
|
@ -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>);
|
57
dapps/node-health/src/types.rs
Normal file
57
dapps/node-health/src/types.rs
Normal 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>,
|
||||||
|
}
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
|
@ -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>,
|
|
||||||
}
|
|
||||||
|
@ -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 }
|
||||||
|
@ -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
|
||||||
|
@ -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![],
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
1
ethcore/native_contracts/res/peer_set.json
Normal file
1
ethcore/native_contracts/res/peer_set.json
Normal 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"}]
|
@ -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;
|
||||||
|
21
ethcore/native_contracts/src/peer_set.rs
Normal file
21
ethcore/native_contracts/src/peer_set.rs
Normal 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"));
|
16
ethcore/node_filter/Cargo.toml
Normal file
16
ethcore/node_filter/Cargo.toml
Normal 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"
|
44
ethcore/node_filter/res/node_filter.json
Normal file
44
ethcore/node_filter/res/node_filter.json
Normal 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" }
|
||||||
|
}
|
||||||
|
}
|
154
ethcore/node_filter/src/lib.rs
Normal file
154
ethcore/node_filter/src/lib.rs
Normal 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
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -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};
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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());
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -62,7 +62,7 @@ impl Tracer for NoopTracer {
|
|||||||
NoopTracer
|
NoopTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traces(self) -> Vec<FlatTrace> {
|
fn drain(self) -> Vec<FlatTrace> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -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" }
|
||||||
|
@ -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 {
|
||||||
|
@ -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());
|
||||||
|
@ -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: ðjson::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 {
|
||||||
|
@ -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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
2
js/package-lock.json
generated
@ -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": {
|
||||||
|
@ -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>",
|
||||||
|
@ -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');
|
||||||
|
@ -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
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
.infoline,
|
.infoline,
|
||||||
.uuidline {
|
.uuidline {
|
||||||
opacity: 0.25;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uuidline {
|
.uuidline {
|
||||||
|
@ -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)]
|
||||||
|
@ -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.
|
||||||
|
@ -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![]),
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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>))
|
||||||
}
|
}
|
||||||
|
@ -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![]),
|
||||||
|
@ -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(),
|
||||||
|
@ -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(),
|
||||||
|
@ -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"
|
||||||
|
@ -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;
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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)),
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()));
|
||||||
|
}
|
||||||
|
@ -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>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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")]
|
||||||
|
2
test.sh
2
test.sh
@ -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
|
||||||
|
@ -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=[]
|
||||||
|
@ -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.
|
||||||
|
@ -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]
|
||||||
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
util/network/src/connection_filter.rs
Normal file
31
util/network/src/connection_filter.rs
Normal 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;
|
||||||
|
}
|
@ -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@"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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};
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
12
util/plain_hasher/Cargo.toml
Normal file
12
util/plain_hasher/Cargo.toml
Normal 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"
|
33
util/plain_hasher/benches/bench.rs
Normal file
33
util/plain_hasher/benches/bench.rs
Normal 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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
55
util/plain_hasher/src/lib.rs
Normal file
55
util/plain_hasher/src/lib.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
@ -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));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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); }
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user