Merge branch 'master' of github.com:ethcore/parity into thread
This commit is contained in:
commit
a6b5aad8c9
53
Cargo.lock
generated
53
Cargo.lock
generated
@ -15,7 +15,9 @@ dependencies = [
|
|||||||
"fdlimit 0.1.0",
|
"fdlimit 0.1.0",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (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)",
|
||||||
|
"rpassword 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -146,6 +148,14 @@ dependencies = [
|
|||||||
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deque"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "docopt"
|
name = "docopt"
|
||||||
version = "0.6.78"
|
version = "0.6.78"
|
||||||
@ -209,6 +219,7 @@ dependencies = [
|
|||||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -228,10 +239,11 @@ dependencies = [
|
|||||||
"ethcore 0.9.99",
|
"ethcore 0.9.99",
|
||||||
"ethcore-util 0.9.99",
|
"ethcore-util 0.9.99",
|
||||||
"ethsync 0.9.99",
|
"ethsync 0.9.99",
|
||||||
"jsonrpc-core 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-http-server 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_codegen 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -285,7 +297,9 @@ dependencies = [
|
|||||||
"heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"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)",
|
||||||
|
"rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -400,7 +414,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-core"
|
name = "jsonrpc-core"
|
||||||
version = "1.2.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -411,11 +425,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-http-server"
|
name = "jsonrpc-http-server"
|
||||||
version = "2.1.0"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-core 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -655,6 +669,16 @@ dependencies = [
|
|||||||
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "0.1.54"
|
version = "0.1.54"
|
||||||
@ -680,6 +704,17 @@ dependencies = [
|
|||||||
"librocksdb-sys 0.2.1 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
"librocksdb-sys 0.2.1 (git+https://github.com/arkpar/rust-rocksdb.git)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpassword"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-crypto"
|
name = "rust-crypto"
|
||||||
version = "0.2.34"
|
version = "0.2.34"
|
||||||
@ -813,6 +848,14 @@ dependencies = [
|
|||||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termios"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.34"
|
version = "0.1.34"
|
||||||
|
28
Cargo.toml
28
Cargo.toml
@ -4,6 +4,10 @@ name = "parity"
|
|||||||
version = "0.9.99"
|
version = "0.9.99"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Ethcore <admin@ethcore.io>"]
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rustc_version = "0.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
@ -12,22 +16,30 @@ rustc-serialize = "0.3"
|
|||||||
docopt = "0.6"
|
docopt = "0.6"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" }
|
ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" }
|
||||||
clippy = { version = "0.0.44", optional = true }
|
|
||||||
ethcore-util = { path = "util" }
|
|
||||||
ethcore = { path = "ethcore" }
|
|
||||||
ethsync = { path = "sync" }
|
|
||||||
ethcore-rpc = { path = "rpc", optional = true }
|
|
||||||
fdlimit = { path = "util/fdlimit" }
|
fdlimit = { path = "util/fdlimit" }
|
||||||
daemonize = "0.2"
|
daemonize = "0.2"
|
||||||
ethcore-devtools = { path = "devtools" }
|
|
||||||
number_prefix = "0.2"
|
number_prefix = "0.2"
|
||||||
|
clippy = { version = "0.0.44", optional = true }
|
||||||
|
ethcore = { path = "ethcore" }
|
||||||
|
ethcore-util = { path = "util" }
|
||||||
|
ethsync = { path = "sync" }
|
||||||
|
ethcore-devtools = { path = "devtools" }
|
||||||
|
ethcore-rpc = { path = "rpc", optional = true }
|
||||||
|
rpassword = "0.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
ethcore = { path = "ethcore", features = ["dev"] }
|
||||||
|
ethcore-util = { path = "util", features = ["dev"] }
|
||||||
|
ethsync = { path = "sync", features = ["dev"] }
|
||||||
|
ethcore-rpc = { path = "rpc", features = ["dev"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rpc"]
|
default = ["rpc"]
|
||||||
rpc = ["ethcore-rpc"]
|
rpc = ["ethcore-rpc"]
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev"]
|
dev = ["ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev"]
|
||||||
|
dev-clippy = ["clippy", "ethcore/clippy", "ethcore-util/clippy", "ethsync/clippy", "ethcore-rpc/clippy"]
|
||||||
travis-beta = ["ethcore/json-tests"]
|
travis-beta = ["ethcore/json-tests"]
|
||||||
travis-nightly = ["ethcore/json-tests", "dev"]
|
travis-nightly = ["ethcore/json-tests", "dev-clippy", "dev"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
path = "parity/main.rs"
|
path = "parity/main.rs"
|
||||||
|
25
build.rs
Normal file
25
build.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate rustc_version;
|
||||||
|
|
||||||
|
use rustc_version::{version_meta, Channel};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Channel::Nightly = version_meta().channel {
|
||||||
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,10 @@ license = "GPL-3.0"
|
|||||||
name = "ethcore"
|
name = "ethcore"
|
||||||
version = "0.9.99"
|
version = "0.9.99"
|
||||||
authors = ["Ethcore <admin@ethcore.io>"]
|
authors = ["Ethcore <admin@ethcore.io>"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rustc_version = "0.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
@ -27,5 +31,5 @@ jit = ["evmjit"]
|
|||||||
evm-debug = []
|
evm-debug = []
|
||||||
json-tests = []
|
json-tests = []
|
||||||
test-heavy = []
|
test-heavy = []
|
||||||
dev = ["clippy"]
|
dev = []
|
||||||
default = []
|
default = []
|
||||||
|
25
ethcore/build.rs
Normal file
25
ethcore/build.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate rustc_version;
|
||||||
|
|
||||||
|
use rustc_version::{version_meta, Channel};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Channel::Nightly = version_meta().channel {
|
||||||
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ pub type LogBloom = H2048;
|
|||||||
/// Constant 2048-bit datum for 0. Often used as a default.
|
/// Constant 2048-bit datum for 0. Often used as a default.
|
||||||
pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]);
|
pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]);
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(enum_variant_names))]
|
||||||
/// Semantic boolean for when a seal/signature is included.
|
/// Semantic boolean for when a seal/signature is included.
|
||||||
pub enum Seal {
|
pub enum Seal {
|
||||||
/// The seal/signature is included.
|
/// The seal/signature is included.
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Blockchain block.
|
//! Blockchain block.
|
||||||
|
|
||||||
#![cfg_attr(feature="dev", allow(ptr_arg))] // Because of &LastHashes -> &Vec<_>
|
#![cfg_attr(all(nightly, feature="dev"), allow(ptr_arg))] // Because of &LastHashes -> &Vec<_>
|
||||||
|
|
||||||
use common::*;
|
use common::*;
|
||||||
use engine::*;
|
use engine::*;
|
||||||
|
@ -121,7 +121,7 @@ struct QueueSignal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QueueSignal {
|
impl QueueSignal {
|
||||||
#[cfg_attr(feature="dev", allow(bool_comparison))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(bool_comparison))]
|
||||||
fn set(&self) {
|
fn set(&self) {
|
||||||
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
|
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
|
||||||
self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message");
|
self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message");
|
||||||
@ -331,6 +331,9 @@ impl BlockQueue {
|
|||||||
|
|
||||||
/// Mark given block and all its children as bad. Stops verification.
|
/// Mark given block and all its children as bad. Stops verification.
|
||||||
pub fn mark_as_bad(&self, block_hashes: &[H256]) {
|
pub fn mark_as_bad(&self, block_hashes: &[H256]) {
|
||||||
|
if block_hashes.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let mut verified_lock = self.verification.verified.lock().unwrap();
|
let mut verified_lock = self.verification.verified.lock().unwrap();
|
||||||
let mut verified = verified_lock.deref_mut();
|
let mut verified = verified_lock.deref_mut();
|
||||||
let mut bad = self.verification.bad.lock().unwrap();
|
let mut bad = self.verification.bad.lock().unwrap();
|
||||||
@ -355,6 +358,9 @@ impl BlockQueue {
|
|||||||
|
|
||||||
/// Mark given block as processed
|
/// Mark given block as processed
|
||||||
pub fn mark_as_good(&self, block_hashes: &[H256]) {
|
pub fn mark_as_good(&self, block_hashes: &[H256]) {
|
||||||
|
if block_hashes.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let mut processing = self.processing.write().unwrap();
|
let mut processing = self.processing.write().unwrap();
|
||||||
for hash in block_hashes {
|
for hash in block_hashes {
|
||||||
processing.remove(&hash);
|
processing.remove(&hash);
|
||||||
|
@ -18,6 +18,7 @@ use util::numbers::{U256,H256};
|
|||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
|
||||||
/// Brief info about inserted block.
|
/// Brief info about inserted block.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct BlockInfo {
|
pub struct BlockInfo {
|
||||||
/// Block hash.
|
/// Block hash.
|
||||||
pub hash: H256,
|
pub hash: H256,
|
||||||
@ -30,6 +31,7 @@ pub struct BlockInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Describes location of newly inserted block.
|
/// Describes location of newly inserted block.
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum BlockLocation {
|
pub enum BlockLocation {
|
||||||
/// It's part of the canon chain.
|
/// It's part of the canon chain.
|
||||||
CanonChain,
|
CanonChain,
|
||||||
@ -42,6 +44,8 @@ pub enum BlockLocation {
|
|||||||
/// Hash of the newest common ancestor with old canon chain.
|
/// Hash of the newest common ancestor with old canon chain.
|
||||||
ancestor: H256,
|
ancestor: H256,
|
||||||
/// Hashes of the blocks between ancestor and this block.
|
/// Hashes of the blocks between ancestor and this block.
|
||||||
route: Vec<H256>
|
enacted: Vec<H256>,
|
||||||
|
/// Hashes of the blocks which were invalidated.
|
||||||
|
retracted: Vec<H256>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ use blockchain::best_block::BestBlock;
|
|||||||
use blockchain::bloom_indexer::BloomIndexer;
|
use blockchain::bloom_indexer::BloomIndexer;
|
||||||
use blockchain::tree_route::TreeRoute;
|
use blockchain::tree_route::TreeRoute;
|
||||||
use blockchain::update::ExtrasUpdate;
|
use blockchain::update::ExtrasUpdate;
|
||||||
use blockchain::CacheSize;
|
use blockchain::{CacheSize, ImportRoute};
|
||||||
|
|
||||||
const BLOOM_INDEX_SIZE: usize = 16;
|
const BLOOM_INDEX_SIZE: usize = 16;
|
||||||
const BLOOM_LEVELS: u8 = 3;
|
const BLOOM_LEVELS: u8 = 3;
|
||||||
@ -419,14 +419,14 @@ impl BlockChain {
|
|||||||
/// Inserts the block into backing cache database.
|
/// Inserts the block into backing cache database.
|
||||||
/// Expects the block to be valid and already verified.
|
/// Expects the block to be valid and already verified.
|
||||||
/// If the block is already known, does nothing.
|
/// If the block is already known, does nothing.
|
||||||
pub fn insert_block(&self, bytes: &[u8], receipts: Vec<Receipt>) {
|
pub fn insert_block(&self, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||||
// create views onto rlp
|
// create views onto rlp
|
||||||
let block = BlockView::new(bytes);
|
let block = BlockView::new(bytes);
|
||||||
let header = block.header_view();
|
let header = block.header_view();
|
||||||
let hash = header.sha3();
|
let hash = header.sha3();
|
||||||
|
|
||||||
if self.is_known(&hash) {
|
if self.is_known(&hash) {
|
||||||
return;
|
return ImportRoute::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
let _lock = self.insert_lock.lock();
|
let _lock = self.insert_lock.lock();
|
||||||
@ -441,8 +441,10 @@ impl BlockChain {
|
|||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||||
info: info
|
info: info.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ImportRoute::from(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies extras update.
|
/// Applies extras update.
|
||||||
@ -565,9 +567,14 @@ impl BlockChain {
|
|||||||
|
|
||||||
match route.blocks.len() {
|
match route.blocks.len() {
|
||||||
0 => BlockLocation::CanonChain,
|
0 => BlockLocation::CanonChain,
|
||||||
_ => BlockLocation::BranchBecomingCanonChain {
|
_ => {
|
||||||
|
let retracted = route.blocks.iter().take(route.index).cloned().collect::<Vec<H256>>();
|
||||||
|
|
||||||
|
BlockLocation::BranchBecomingCanonChain {
|
||||||
ancestor: route.ancestor,
|
ancestor: route.ancestor,
|
||||||
route: route.blocks.into_iter().skip(route.index).collect()
|
enacted: route.blocks.into_iter().skip(route.index).collect(),
|
||||||
|
retracted: retracted.into_iter().rev().collect(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -588,11 +595,11 @@ impl BlockChain {
|
|||||||
BlockLocation::CanonChain => {
|
BlockLocation::CanonChain => {
|
||||||
block_hashes.insert(number, info.hash.clone());
|
block_hashes.insert(number, info.hash.clone());
|
||||||
},
|
},
|
||||||
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref route } => {
|
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref enacted, .. } => {
|
||||||
let ancestor_number = self.block_number(ancestor).unwrap();
|
let ancestor_number = self.block_number(ancestor).unwrap();
|
||||||
let start_number = ancestor_number + 1;
|
let start_number = ancestor_number + 1;
|
||||||
|
|
||||||
for (index, hash) in route.iter().cloned().enumerate() {
|
for (index, hash) in enacted.iter().cloned().enumerate() {
|
||||||
block_hashes.insert(start_number + index as BlockNumber, hash);
|
block_hashes.insert(start_number + index as BlockNumber, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -677,11 +684,11 @@ impl BlockChain {
|
|||||||
ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
|
ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
|
||||||
.add_bloom(&header.log_bloom(), header.number() as usize)
|
.add_bloom(&header.log_bloom(), header.number() as usize)
|
||||||
},
|
},
|
||||||
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref route } => {
|
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref enacted, .. } => {
|
||||||
let ancestor_number = self.block_number(ancestor).unwrap();
|
let ancestor_number = self.block_number(ancestor).unwrap();
|
||||||
let start_number = ancestor_number + 1;
|
let start_number = ancestor_number + 1;
|
||||||
|
|
||||||
let mut blooms: Vec<H2048> = route.iter()
|
let mut blooms: Vec<H2048> = enacted.iter()
|
||||||
.map(|hash| self.block(hash).unwrap())
|
.map(|hash| self.block(hash).unwrap())
|
||||||
.map(|bytes| BlockView::new(&bytes).header_view().log_bloom())
|
.map(|bytes| BlockView::new(&bytes).header_view().log_bloom())
|
||||||
.collect();
|
.collect();
|
||||||
@ -841,7 +848,7 @@ mod tests {
|
|||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
use util::hash::*;
|
use util::hash::*;
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
use blockchain::{BlockProvider, BlockChain, BlockChainConfig, ImportRoute};
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use devtools::*;
|
use devtools::*;
|
||||||
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
||||||
@ -900,7 +907,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
|
||||||
fn test_find_uncles() {
|
fn test_find_uncles() {
|
||||||
let mut canon_chain = ChainGenerator::default();
|
let mut canon_chain = ChainGenerator::default();
|
||||||
let mut finalizer = BlockFinalizer::default();
|
let mut finalizer = BlockFinalizer::default();
|
||||||
@ -938,7 +945,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
|
||||||
fn test_small_fork() {
|
fn test_small_fork() {
|
||||||
let mut canon_chain = ChainGenerator::default();
|
let mut canon_chain = ChainGenerator::default();
|
||||||
let mut finalizer = BlockFinalizer::default();
|
let mut finalizer = BlockFinalizer::default();
|
||||||
@ -959,10 +966,30 @@ mod tests {
|
|||||||
|
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||||
bc.insert_block(&b1, vec![]);
|
let ir1 = bc.insert_block(&b1, vec![]);
|
||||||
bc.insert_block(&b2, vec![]);
|
let ir2 = bc.insert_block(&b2, vec![]);
|
||||||
bc.insert_block(&b3a, vec![]);
|
let ir3b = bc.insert_block(&b3b, vec![]);
|
||||||
bc.insert_block(&b3b, vec![]);
|
let ir3a = bc.insert_block(&b3a, vec![]);
|
||||||
|
|
||||||
|
assert_eq!(ir1, ImportRoute {
|
||||||
|
enacted: vec![b1_hash],
|
||||||
|
retracted: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(ir2, ImportRoute {
|
||||||
|
enacted: vec![b2_hash],
|
||||||
|
retracted: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(ir3b, ImportRoute {
|
||||||
|
enacted: vec![b3b_hash],
|
||||||
|
retracted: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(ir3a, ImportRoute {
|
||||||
|
enacted: vec![b3a_hash],
|
||||||
|
retracted: vec![b3b_hash],
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(bc.best_block_hash(), best_block_hash);
|
assert_eq!(bc.best_block_hash(), best_block_hash);
|
||||||
assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0);
|
assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0);
|
||||||
|
@ -29,7 +29,7 @@ pub trait ChainIterator: Iterator + Sized {
|
|||||||
/// Blocks generated by fork will have lower difficulty than current chain.
|
/// Blocks generated by fork will have lower difficulty than current chain.
|
||||||
fn fork(&self, fork_number: usize) -> Fork<Self> where Self: Clone;
|
fn fork(&self, fork_number: usize) -> Fork<Self> where Self: Clone;
|
||||||
/// Should be called to make every consecutive block have given bloom.
|
/// Should be called to make every consecutive block have given bloom.
|
||||||
fn with_bloom<'a>(&'a mut self, bloom: H2048) -> Bloom<'a, Self>;
|
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self>;
|
||||||
/// Should be called to complete block. Without complete, block may have incorrect hash.
|
/// Should be called to complete block. Without complete, block may have incorrect hash.
|
||||||
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>;
|
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>;
|
||||||
/// Completes and generates block.
|
/// Completes and generates block.
|
||||||
@ -44,7 +44,7 @@ impl<I> ChainIterator for I where I: Iterator + Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_bloom<'a>(&'a mut self, bloom: H2048) -> Bloom<'a, Self> {
|
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self> {
|
||||||
Bloom {
|
Bloom {
|
||||||
iter: self,
|
iter: self,
|
||||||
bloom: bloom
|
bloom: bloom
|
||||||
|
119
ethcore/src/blockchain/import_route.rs
Normal file
119
ethcore/src/blockchain/import_route.rs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Import route.
|
||||||
|
|
||||||
|
use util::hash::H256;
|
||||||
|
use blockchain::block_info::{BlockInfo, BlockLocation};
|
||||||
|
|
||||||
|
/// Import route for newly inserted block.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct ImportRoute {
|
||||||
|
/// Blocks that were invalidated by new block.
|
||||||
|
pub retracted: Vec<H256>,
|
||||||
|
/// Blocks that were validated by new block.
|
||||||
|
pub enacted: Vec<H256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImportRoute {
|
||||||
|
pub fn none() -> Self {
|
||||||
|
ImportRoute {
|
||||||
|
retracted: vec![],
|
||||||
|
enacted: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BlockInfo> for ImportRoute {
|
||||||
|
fn from(info: BlockInfo) -> ImportRoute {
|
||||||
|
match info.location {
|
||||||
|
BlockLocation::CanonChain => ImportRoute {
|
||||||
|
retracted: vec![],
|
||||||
|
enacted: vec![info.hash],
|
||||||
|
},
|
||||||
|
BlockLocation::Branch => ImportRoute::none(),
|
||||||
|
BlockLocation::BranchBecomingCanonChain { mut enacted, retracted, .. } => {
|
||||||
|
enacted.push(info.hash);
|
||||||
|
ImportRoute {
|
||||||
|
retracted: retracted,
|
||||||
|
enacted: enacted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use util::hash::H256;
|
||||||
|
use util::numbers::U256;
|
||||||
|
use blockchain::block_info::{BlockInfo, BlockLocation};
|
||||||
|
use blockchain::ImportRoute;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_route_none() {
|
||||||
|
assert_eq!(ImportRoute::none(), ImportRoute {
|
||||||
|
enacted: vec![],
|
||||||
|
retracted: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_route_branch() {
|
||||||
|
let info = BlockInfo {
|
||||||
|
hash: H256::from(U256::from(1)),
|
||||||
|
number: 0,
|
||||||
|
total_difficulty: U256::from(0),
|
||||||
|
location: BlockLocation::Branch,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(ImportRoute::from(info), ImportRoute::none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_route_canon_chain() {
|
||||||
|
let info = BlockInfo {
|
||||||
|
hash: H256::from(U256::from(1)),
|
||||||
|
number: 0,
|
||||||
|
total_difficulty: U256::from(0),
|
||||||
|
location: BlockLocation::CanonChain,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(ImportRoute::from(info), ImportRoute {
|
||||||
|
retracted: vec![],
|
||||||
|
enacted: vec![H256::from(U256::from(1))],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_route_branch_becoming_canon_chain() {
|
||||||
|
let info = BlockInfo {
|
||||||
|
hash: H256::from(U256::from(2)),
|
||||||
|
number: 0,
|
||||||
|
total_difficulty: U256::from(0),
|
||||||
|
location: BlockLocation::BranchBecomingCanonChain {
|
||||||
|
ancestor: H256::from(U256::from(0)),
|
||||||
|
enacted: vec![H256::from(U256::from(1))],
|
||||||
|
retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(ImportRoute::from(info), ImportRoute {
|
||||||
|
retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))],
|
||||||
|
enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,9 @@ mod tree_route;
|
|||||||
mod update;
|
mod update;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod generator;
|
mod generator;
|
||||||
|
mod import_route;
|
||||||
|
|
||||||
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
||||||
pub use self::cache::CacheSize;
|
pub use self::cache::CacheSize;
|
||||||
pub use self::tree_route::TreeRoute;
|
pub use self::tree_route::TreeRoute;
|
||||||
|
pub use self::import_route::ImportRoute;
|
||||||
|
@ -87,6 +87,8 @@ pub struct ClientConfig {
|
|||||||
pub blockchain: BlockChainConfig,
|
pub blockchain: BlockChainConfig,
|
||||||
/// Prefer journal rather than archive.
|
/// Prefer journal rather than archive.
|
||||||
pub prefer_journal: bool,
|
pub prefer_journal: bool,
|
||||||
|
/// The name of the client instance.
|
||||||
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ClientConfig {
|
impl Default for ClientConfig {
|
||||||
@ -95,6 +97,7 @@ impl Default for ClientConfig {
|
|||||||
queue: Default::default(),
|
queue: Default::default(),
|
||||||
blockchain: Default::default(),
|
blockchain: Default::default(),
|
||||||
prefer_journal: false,
|
prefer_journal: false,
|
||||||
|
name: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,6 +141,9 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
/// Get block total difficulty.
|
/// Get block total difficulty.
|
||||||
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;
|
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;
|
||||||
|
|
||||||
|
/// Get address nonce.
|
||||||
|
fn nonce(&self, address: &Address) -> U256;
|
||||||
|
|
||||||
/// Get block hash.
|
/// Get block hash.
|
||||||
fn block_hash(&self, id: BlockId) -> Option<H256>;
|
fn block_hash(&self, id: BlockId) -> Option<H256>;
|
||||||
|
|
||||||
@ -179,6 +185,13 @@ pub trait BlockChainClient : Sync + Send {
|
|||||||
|
|
||||||
/// Returns logs matching given filter.
|
/// Returns logs matching given filter.
|
||||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
||||||
|
|
||||||
|
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
||||||
|
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>>;
|
||||||
|
|
||||||
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
|
fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug, Eq, PartialEq)]
|
#[derive(Default, Clone, Debug, Eq, PartialEq)]
|
||||||
@ -190,6 +203,8 @@ pub struct ClientReport {
|
|||||||
pub transactions_applied: usize,
|
pub transactions_applied: usize,
|
||||||
/// How much gas has been processed so far.
|
/// How much gas has been processed so far.
|
||||||
pub gas_processed: U256,
|
pub gas_processed: U256,
|
||||||
|
/// Memory used by state DB
|
||||||
|
pub state_db_mem: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientReport {
|
impl ClientReport {
|
||||||
@ -222,7 +237,7 @@ pub struct Client<V = CanonVerifier> where V: Verifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const HISTORY: u64 = 1000;
|
const HISTORY: u64 = 1000;
|
||||||
const CLIENT_DB_VER_STR: &'static str = "4.0";
|
const CLIENT_DB_VER_STR: &'static str = "5.1";
|
||||||
|
|
||||||
impl Client<CanonVerifier> {
|
impl Client<CanonVerifier> {
|
||||||
/// Create a new client with given spec and DB path.
|
/// Create a new client with given spec and DB path.
|
||||||
@ -364,18 +379,14 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
bad_blocks.insert(header.hash());
|
bad_blocks.insert(header.hash());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let closed_block = self.check_and_close_block(&block);
|
let closed_block = self.check_and_close_block(&block);
|
||||||
if let Err(_) = closed_block {
|
if let Err(_) = closed_block {
|
||||||
bad_blocks.insert(header.hash());
|
bad_blocks.insert(header.hash());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert block
|
|
||||||
let closed_block = closed_block.unwrap();
|
|
||||||
self.chain.insert_block(&block.bytes, closed_block.block().receipts().clone());
|
|
||||||
good_blocks.push(header.hash());
|
good_blocks.push(header.hash());
|
||||||
|
|
||||||
|
// Are we committing an era?
|
||||||
let ancient = if header.number() >= HISTORY {
|
let ancient = if header.number() >= HISTORY {
|
||||||
let n = header.number() - HISTORY;
|
let n = header.number() - HISTORY;
|
||||||
Some((n, self.chain.block_hash(n).unwrap()))
|
Some((n, self.chain.block_hash(n).unwrap()))
|
||||||
@ -384,10 +395,16 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Commit results
|
// Commit results
|
||||||
|
let closed_block = closed_block.unwrap();
|
||||||
|
let receipts = closed_block.block().receipts().clone();
|
||||||
closed_block.drain()
|
closed_block.drain()
|
||||||
.commit(header.number(), &header.hash(), ancient)
|
.commit(header.number(), &header.hash(), ancient)
|
||||||
.expect("State DB commit failed.");
|
.expect("State DB commit failed.");
|
||||||
|
|
||||||
|
// And update the chain after commit to prevent race conditions
|
||||||
|
// (when something is in chain but you are not able to fetch details)
|
||||||
|
self.chain.insert_block(&block.bytes, receipts);
|
||||||
|
|
||||||
self.report.write().unwrap().accrue_block(&block);
|
self.report.write().unwrap().accrue_block(&block);
|
||||||
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
||||||
}
|
}
|
||||||
@ -396,15 +413,21 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
let bad_blocks = bad_blocks.into_iter().collect::<Vec<H256>>();
|
let bad_blocks = bad_blocks.into_iter().collect::<Vec<H256>>();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
if !bad_blocks.is_empty() {
|
||||||
self.block_queue.mark_as_bad(&bad_blocks);
|
self.block_queue.mark_as_bad(&bad_blocks);
|
||||||
|
}
|
||||||
|
if !good_blocks.is_empty() {
|
||||||
self.block_queue.mark_as_good(&good_blocks);
|
self.block_queue.mark_as_good(&good_blocks);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if !good_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
if !good_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
||||||
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
|
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
|
||||||
good: good_blocks,
|
good: good_blocks,
|
||||||
bad: bad_blocks,
|
bad: bad_blocks,
|
||||||
|
// TODO [todr] were to take those from?
|
||||||
|
retracted: vec![],
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,7 +451,9 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
|
|
||||||
/// Get the report.
|
/// Get the report.
|
||||||
pub fn report(&self) -> ClientReport {
|
pub fn report(&self) -> ClientReport {
|
||||||
self.report.read().unwrap().clone()
|
let mut report = self.report.read().unwrap().clone();
|
||||||
|
report.state_db_mem = self.state_db.lock().unwrap().mem_used();
|
||||||
|
report
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tick the client.
|
/// Tick the client.
|
||||||
@ -500,39 +525,6 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
trace!("Sealing: number={}, hash={}, diff={}", b.hash(), b.block().header().difficulty(), b.block().header().number());
|
trace!("Sealing: number={}, hash={}, diff={}", b.hash(), b.block().header().difficulty(), b.block().header().number());
|
||||||
*self.sealing_block.lock().unwrap() = Some(b);
|
*self.sealing_block.lock().unwrap() = Some(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
|
||||||
pub fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
|
||||||
if self.sealing_block.lock().unwrap().is_none() {
|
|
||||||
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
|
|
||||||
// TODO: Above should be on a timer that resets after two blocks have arrived without being asked for.
|
|
||||||
self.prepare_sealing();
|
|
||||||
}
|
|
||||||
&self.sealing_block
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
|
||||||
/// Will check the seal, but not actually insert the block into the chain.
|
|
||||||
pub fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
|
||||||
let mut maybe_b = self.sealing_block.lock().unwrap();
|
|
||||||
match *maybe_b {
|
|
||||||
Some(ref b) if b.hash() == pow_hash => {}
|
|
||||||
_ => { return Err(Error::PowHashInvalid); }
|
|
||||||
}
|
|
||||||
|
|
||||||
let b = maybe_b.take();
|
|
||||||
match b.unwrap().try_seal(self.engine.deref().deref(), seal) {
|
|
||||||
Err(old) => {
|
|
||||||
*maybe_b = Some(old);
|
|
||||||
Err(Error::PowInvalid)
|
|
||||||
}
|
|
||||||
Ok(sealed) => {
|
|
||||||
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
|
|
||||||
try!(self.import_block(sealed.rlp_bytes()));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: need MinerService MinerIoHandler
|
// TODO: need MinerService MinerIoHandler
|
||||||
@ -572,6 +564,10 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_details(&hash)).map(|d| d.total_difficulty)
|
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_details(&hash)).map(|d| d.total_difficulty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn nonce(&self, address: &Address) -> U256 {
|
||||||
|
self.state().nonce(address)
|
||||||
|
}
|
||||||
|
|
||||||
fn block_hash(&self, id: BlockId) -> Option<H256> {
|
fn block_hash(&self, id: BlockId) -> Option<H256> {
|
||||||
Self::block_hash(&self.chain, id)
|
Self::block_hash(&self.chain, id)
|
||||||
}
|
}
|
||||||
@ -682,6 +678,39 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
||||||
|
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
||||||
|
if self.sealing_block.lock().unwrap().is_none() {
|
||||||
|
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
|
||||||
|
// TODO: Above should be on a timer that resets after two blocks have arrived without being asked for.
|
||||||
|
self.prepare_sealing();
|
||||||
|
}
|
||||||
|
&self.sealing_block
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
|
fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
||||||
|
let mut maybe_b = self.sealing_block.lock().unwrap();
|
||||||
|
match *maybe_b {
|
||||||
|
Some(ref b) if b.hash() == pow_hash => {}
|
||||||
|
_ => { return Err(Error::PowHashInvalid); }
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = maybe_b.take();
|
||||||
|
match b.unwrap().try_seal(self.engine.deref().deref(), seal) {
|
||||||
|
Err(old) => {
|
||||||
|
*maybe_b = Some(old);
|
||||||
|
Err(Error::PowInvalid)
|
||||||
|
}
|
||||||
|
Ok(sealed) => {
|
||||||
|
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
|
||||||
|
try!(self.import_block(sealed.rlp_bytes()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MayPanic for Client {
|
impl MayPanic for Client {
|
||||||
|
@ -202,7 +202,7 @@ impl Engine for Ethash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self
|
#[cfg_attr(all(nightly, feature="dev"), allow(wrong_self_convention))] // to_ethash should take self
|
||||||
impl Ethash {
|
impl Ethash {
|
||||||
fn calculate_difficuty(&self, header: &Header, parent: &Header) -> U256 {
|
fn calculate_difficuty(&self, header: &Header, parent: &Header) -> U256 {
|
||||||
const EXP_DIFF_PERIOD: u64 = 100000;
|
const EXP_DIFF_PERIOD: u64 = 100000;
|
||||||
|
@ -243,7 +243,7 @@ struct CodeReader<'a> {
|
|||||||
code: &'a Bytes
|
code: &'a Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(len_without_is_empty))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(len_without_is_empty))]
|
||||||
impl<'a> CodeReader<'a> {
|
impl<'a> CodeReader<'a> {
|
||||||
/// Get `no_of_bytes` from code and convert to U256. Move PC
|
/// Get `no_of_bytes` from code and convert to U256. Move PC
|
||||||
fn read(&mut self, no_of_bytes: usize) -> U256 {
|
fn read(&mut self, no_of_bytes: usize) -> U256 {
|
||||||
@ -258,7 +258,7 @@ impl<'a> CodeReader<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(enum_variant_names))]
|
||||||
enum InstructionCost {
|
enum InstructionCost {
|
||||||
Gas(U256),
|
Gas(U256),
|
||||||
GasMem(U256, U256),
|
GasMem(U256, U256),
|
||||||
@ -347,7 +347,7 @@ impl evm::Evm for Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
|
||||||
fn get_gas_cost_mem(&self,
|
fn get_gas_cost_mem(&self,
|
||||||
ext: &evm::Ext,
|
ext: &evm::Ext,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
|
@ -25,9 +25,8 @@ struct FakeLogEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||||
#[cfg_attr(feature="dev", allow(enum_variant_names))] // Common prefix is C ;)
|
|
||||||
enum FakeCallType {
|
enum FakeCallType {
|
||||||
CALL, CREATE
|
Call, Create
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||||
@ -94,7 +93,7 @@ impl Ext for FakeExt {
|
|||||||
|
|
||||||
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult {
|
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult {
|
||||||
self.calls.insert(FakeCall {
|
self.calls.insert(FakeCall {
|
||||||
call_type: FakeCallType::CREATE,
|
call_type: FakeCallType::Create,
|
||||||
gas: *gas,
|
gas: *gas,
|
||||||
sender_address: None,
|
sender_address: None,
|
||||||
receive_address: None,
|
receive_address: None,
|
||||||
@ -115,7 +114,7 @@ impl Ext for FakeExt {
|
|||||||
_output: &mut [u8]) -> MessageCallResult {
|
_output: &mut [u8]) -> MessageCallResult {
|
||||||
|
|
||||||
self.calls.insert(FakeCall {
|
self.calls.insert(FakeCall {
|
||||||
call_type: FakeCallType::CALL,
|
call_type: FakeCallType::Call,
|
||||||
gas: *gas,
|
gas: *gas,
|
||||||
sender_address: Some(sender_address.clone()),
|
sender_address: Some(sender_address.clone()),
|
||||||
receive_address: Some(receive_address.clone()),
|
receive_address: Some(receive_address.clone()),
|
||||||
@ -909,7 +908,7 @@ fn test_calls(factory: super::Factory) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_set_contains(&ext.calls, &FakeCall {
|
assert_set_contains(&ext.calls, &FakeCall {
|
||||||
call_type: FakeCallType::CALL,
|
call_type: FakeCallType::Call,
|
||||||
gas: U256::from(2556),
|
gas: U256::from(2556),
|
||||||
sender_address: Some(address.clone()),
|
sender_address: Some(address.clone()),
|
||||||
receive_address: Some(code_address.clone()),
|
receive_address: Some(code_address.clone()),
|
||||||
@ -918,7 +917,7 @@ fn test_calls(factory: super::Factory) {
|
|||||||
code_address: Some(code_address.clone())
|
code_address: Some(code_address.clone())
|
||||||
});
|
});
|
||||||
assert_set_contains(&ext.calls, &FakeCall {
|
assert_set_contains(&ext.calls, &FakeCall {
|
||||||
call_type: FakeCallType::CALL,
|
call_type: FakeCallType::Call,
|
||||||
gas: U256::from(2556),
|
gas: U256::from(2556),
|
||||||
sender_address: Some(address.clone()),
|
sender_address: Some(address.clone()),
|
||||||
receive_address: Some(address.clone()),
|
receive_address: Some(address.clone()),
|
||||||
|
@ -188,7 +188,7 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
self.state.code(address).unwrap_or_else(|| vec![])
|
self.state.code(address).unwrap_or_else(|| vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(match_ref_pats))]
|
||||||
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
|
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
|
||||||
match &mut self.output {
|
match &mut self.output {
|
||||||
&mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe {
|
&mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe {
|
||||||
|
@ -15,16 +15,16 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![cfg_attr(feature="dev", feature(plugin))]
|
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
|
||||||
#![cfg_attr(feature="dev", plugin(clippy))]
|
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
|
||||||
|
|
||||||
// Clippy config
|
// Clippy config
|
||||||
// TODO [todr] not really sure
|
// TODO [todr] not really sure
|
||||||
#![cfg_attr(feature="dev", allow(needless_range_loop))]
|
#![cfg_attr(all(nightly, feature="dev"), allow(needless_range_loop))]
|
||||||
// Shorter than if-else
|
// Shorter than if-else
|
||||||
#![cfg_attr(feature="dev", allow(match_bool))]
|
#![cfg_attr(all(nightly, feature="dev"), allow(match_bool))]
|
||||||
// Keeps consistency (all lines with `.clone()`) and helpful when changing ref to non-ref.
|
// Keeps consistency (all lines with `.clone()`) and helpful when changing ref to non-ref.
|
||||||
#![cfg_attr(feature="dev", allow(clone_on_copy))]
|
#![cfg_attr(all(nightly, feature="dev"), allow(clone_on_copy))]
|
||||||
|
|
||||||
//! Ethcore library
|
//! Ethcore library
|
||||||
//!
|
//!
|
||||||
|
@ -31,6 +31,8 @@ pub enum SyncMessage {
|
|||||||
good: Vec<H256>,
|
good: Vec<H256>,
|
||||||
/// Hashes of blocks not imported to blockchain
|
/// Hashes of blocks not imported to blockchain
|
||||||
bad: Vec<H256>,
|
bad: Vec<H256>,
|
||||||
|
/// Hashes of blocks that were removed from canonical chain
|
||||||
|
retracted: Vec<H256>,
|
||||||
},
|
},
|
||||||
/// A block is ready
|
/// A block is ready
|
||||||
BlockVerified,
|
BlockVerified,
|
||||||
@ -115,12 +117,11 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(single_match))]
|
||||||
#[cfg_attr(feature="dev", allow(single_match))]
|
|
||||||
fn message(&self, io: &IoContext<NetSyncMessage>, net_message: &NetSyncMessage) {
|
fn message(&self, io: &IoContext<NetSyncMessage>, net_message: &NetSyncMessage) {
|
||||||
if let &UserMessage(ref message) = net_message {
|
if let UserMessage(ref message) = *net_message {
|
||||||
match message {
|
match *message {
|
||||||
&SyncMessage::BlockVerified => {
|
SyncMessage::BlockVerified => {
|
||||||
self.client.import_verified_blocks(&io.channel());
|
self.client.import_verified_blocks(&io.channel());
|
||||||
},
|
},
|
||||||
_ => {}, // ignore other messages
|
_ => {}, // ignore other messages
|
||||||
|
@ -99,7 +99,7 @@ pub struct Spec {
|
|||||||
genesis_state: PodState,
|
genesis_state: PodState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))] // because to_engine(self) should be to_engine(&self)
|
#[cfg_attr(all(nightly, feature="dev"), allow(wrong_self_convention))] // because to_engine(self) should be to_engine(&self)
|
||||||
impl Spec {
|
impl Spec {
|
||||||
/// Convert this object into a boxed Engine of the right underlying type.
|
/// Convert this object into a boxed Engine of the right underlying type.
|
||||||
// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
|
// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
|
||||||
|
@ -224,7 +224,7 @@ impl State {
|
|||||||
|
|
||||||
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
|
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
|
||||||
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
||||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(match_ref_pats))]
|
||||||
pub fn commit_into(db: &mut HashDB, root: &mut H256, accounts: &mut HashMap<Address, Option<Account>>) {
|
pub fn commit_into(db: &mut HashDB, root: &mut H256, accounts: &mut HashMap<Address, Option<Account>>) {
|
||||||
// first, commit the sub trees.
|
// first, commit the sub trees.
|
||||||
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
|
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
|
||||||
|
@ -80,7 +80,7 @@ impl Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FromJson for SignedTransaction {
|
impl FromJson for SignedTransaction {
|
||||||
#[cfg_attr(feature="dev", allow(single_char_pattern))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(single_char_pattern))]
|
||||||
fn from_json(json: &Json) -> SignedTransaction {
|
fn from_json(json: &Json) -> SignedTransaction {
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: xjson!(&json["nonce"]),
|
nonce: xjson!(&json["nonce"]),
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
pub mod verification;
|
pub mod verification;
|
||||||
pub mod verifier;
|
pub mod verifier;
|
||||||
mod canon_verifier;
|
mod canon_verifier;
|
||||||
|
#[cfg(test)]
|
||||||
mod noop_verifier;
|
mod noop_verifier;
|
||||||
|
|
||||||
pub use self::verification::*;
|
pub use self::verification::*;
|
||||||
pub use self::verifier::Verifier;
|
pub use self::verifier::Verifier;
|
||||||
pub use self::canon_verifier::CanonVerifier;
|
pub use self::canon_verifier::CanonVerifier;
|
||||||
|
#[cfg(test)]
|
||||||
pub use self::noop_verifier::NoopVerifier;
|
pub use self::noop_verifier::NoopVerifier;
|
||||||
|
2
hook.sh
2
hook.sh
@ -1,3 +1,3 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
echo "#!/bin/sh\ncargo test -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity --features dev" > ./.git/hooks/pre-push
|
echo "#!/bin/sh\ncargo test -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity --features dev-clippy" > ./.git/hooks/pre-push
|
||||||
chmod +x ./.git/hooks/pre-push
|
chmod +x ./.git/hooks/pre-push
|
||||||
|
199
parity/main.rs
199
parity/main.rs
@ -17,8 +17,8 @@
|
|||||||
//! Ethcore client application.
|
//! Ethcore client application.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![cfg_attr(feature="dev", feature(plugin))]
|
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
|
||||||
#![cfg_attr(feature="dev", plugin(clippy))]
|
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
@ -32,6 +32,7 @@ extern crate fdlimit;
|
|||||||
extern crate daemonize;
|
extern crate daemonize;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate number_prefix;
|
extern crate number_prefix;
|
||||||
|
extern crate rpassword;
|
||||||
|
|
||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
extern crate ethcore_rpc as rpc;
|
extern crate ethcore_rpc as rpc;
|
||||||
@ -43,16 +44,26 @@ use std::path::PathBuf;
|
|||||||
use env_logger::LogBuilder;
|
use env_logger::LogBuilder;
|
||||||
use ctrlc::CtrlC;
|
use ctrlc::CtrlC;
|
||||||
use util::*;
|
use util::*;
|
||||||
use util::panics::MayPanic;
|
use util::panics::{MayPanic, ForwardPanic, PanicHandler};
|
||||||
use ethcore::spec::*;
|
use ethcore::spec::*;
|
||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
use ethcore::service::{ClientService, NetSyncMessage};
|
use ethcore::service::{ClientService, NetSyncMessage};
|
||||||
use ethcore::ethereum;
|
use ethcore::ethereum;
|
||||||
use ethsync::{EthSync, SyncConfig};
|
use ethsync::{EthSync, SyncConfig, SyncStatusProvider};
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use daemonize::Daemonize;
|
use daemonize::Daemonize;
|
||||||
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
||||||
|
|
||||||
|
fn die_with_message(msg: &str) -> ! {
|
||||||
|
println!("ERROR: {}", msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! die {
|
||||||
|
($($arg:tt)*) => (die_with_message(&format!("{}", format_args!($($arg)*))));
|
||||||
|
}
|
||||||
|
|
||||||
const USAGE: &'static str = r#"
|
const USAGE: &'static str = r#"
|
||||||
Parity. Ethereum Client.
|
Parity. Ethereum Client.
|
||||||
By Wood/Paronyan/Kotewicz/Drwięga/Volf.
|
By Wood/Paronyan/Kotewicz/Drwięga/Volf.
|
||||||
@ -60,15 +71,20 @@ Parity. Ethereum Client.
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
parity daemon <pid-file> [options] [ --no-bootstrap | <enode>... ]
|
parity daemon <pid-file> [options] [ --no-bootstrap | <enode>... ]
|
||||||
|
parity account (new | list)
|
||||||
parity [options] [ --no-bootstrap | <enode>... ]
|
parity [options] [ --no-bootstrap | <enode>... ]
|
||||||
|
|
||||||
Options:
|
Protocol Options:
|
||||||
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
--chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file
|
||||||
or frontier, mainnet, morden, or testnet [default: frontier].
|
or olympic, frontier, homestead, mainnet, morden, or testnet [default: homestead].
|
||||||
|
--testnet Equivalent to --chain testnet (geth-compatible).
|
||||||
|
--networkid INDEX Override the network identifier from the chain we are on.
|
||||||
--archive Client should not prune the state/storage trie.
|
--archive Client should not prune the state/storage trie.
|
||||||
-d --db-path PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
-d --datadir PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
||||||
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
||||||
|
--identity NAME Specify your node's name.
|
||||||
|
|
||||||
|
Networking Options:
|
||||||
--no-bootstrap Don't bother trying to connect to any nodes initially.
|
--no-bootstrap Don't bother trying to connect to any nodes initially.
|
||||||
--listen-address URL Specify the IP/port on which to listen for peers [default: 0.0.0.0:30304].
|
--listen-address URL Specify the IP/port on which to listen for peers [default: 0.0.0.0:30304].
|
||||||
--public-address URL Specify the IP/port on which peers may connect.
|
--public-address URL Specify the IP/port on which peers may connect.
|
||||||
@ -78,18 +94,32 @@ Options:
|
|||||||
--no-upnp Disable trying to figure out the correct public adderss over UPnP.
|
--no-upnp Disable trying to figure out the correct public adderss over UPnP.
|
||||||
--node-key KEY Specify node secret key, either as 64-character hex string or input to SHA3 operation.
|
--node-key KEY Specify node secret key, either as 64-character hex string or input to SHA3 operation.
|
||||||
|
|
||||||
|
API and Console Options:
|
||||||
|
-j --jsonrpc Enable the JSON-RPC API sever.
|
||||||
|
--jsonrpc-addr HOST Specify the hostname portion of the JSONRPC API server [default: 127.0.0.1].
|
||||||
|
--jsonrpc-port PORT Specify the port portion of the JSONRPC API server [default: 8545].
|
||||||
|
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
||||||
|
--jsonrpc-apis APIS Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited
|
||||||
|
list of API name. Possible name are web3, eth and net. [default: web3,eth,net].
|
||||||
|
--rpc Equivalent to --jsonrpc (geth-compatible).
|
||||||
|
--rpcaddr HOST Equivalent to --jsonrpc-addr HOST (geth-compatible).
|
||||||
|
--rpcport PORT Equivalent to --jsonrpc-port PORT (geth-compatible).
|
||||||
|
--rpcapi APIS Equivalent to --jsonrpc-apis APIS (geth-compatible).
|
||||||
|
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL (geth-compatible).
|
||||||
|
|
||||||
|
Sealing/Mining Options:
|
||||||
|
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
||||||
|
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
||||||
|
--extradata STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
||||||
|
|
||||||
|
Memory Footprint Options:
|
||||||
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384].
|
--cache-pref-size BYTES Specify the prefered size of the blockchain cache in bytes [default: 16384].
|
||||||
--cache-max-size BYTES Specify the maximum size of the blockchain cache in bytes [default: 262144].
|
--cache-max-size BYTES Specify the maximum size of the blockchain cache in bytes [default: 262144].
|
||||||
--queue-max-size BYTES Specify the maximum size of memory to use for block queue [default: 52428800].
|
--queue-max-size BYTES Specify the maximum size of memory to use for block queue [default: 52428800].
|
||||||
|
--cache MEGABYTES Set total amount of cache to use for the entire system, mutually exclusive with
|
||||||
|
other cache options (geth-compatible).
|
||||||
|
|
||||||
-j --jsonrpc Enable the JSON-RPC API sever.
|
Miscellaneous Options:
|
||||||
--jsonrpc-url URL Specify URL for JSON-RPC API server [default: 127.0.0.1:8545].
|
|
||||||
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses [default: null].
|
|
||||||
|
|
||||||
--author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards
|
|
||||||
from sealed blocks [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
|
|
||||||
--extra-data STRING Specify a custom extra-data for authored blocks, no more than 32 characters.
|
|
||||||
|
|
||||||
-l --logging LOGGING Specify the logging level.
|
-l --logging LOGGING Specify the logging level.
|
||||||
-v --version Show information about version.
|
-v --version Show information about version.
|
||||||
-h --help Show this screen.
|
-h --help Show this screen.
|
||||||
@ -98,17 +128,24 @@ Options:
|
|||||||
#[derive(Debug, RustcDecodable)]
|
#[derive(Debug, RustcDecodable)]
|
||||||
struct Args {
|
struct Args {
|
||||||
cmd_daemon: bool,
|
cmd_daemon: bool,
|
||||||
|
cmd_account: bool,
|
||||||
|
cmd_new: bool,
|
||||||
|
cmd_list: bool,
|
||||||
arg_pid_file: String,
|
arg_pid_file: String,
|
||||||
arg_enode: Vec<String>,
|
arg_enode: Vec<String>,
|
||||||
flag_chain: String,
|
flag_chain: String,
|
||||||
flag_db_path: String,
|
flag_testnet: bool,
|
||||||
|
flag_datadir: String,
|
||||||
|
flag_networkid: Option<String>,
|
||||||
|
flag_identity: String,
|
||||||
|
flag_cache: Option<usize>,
|
||||||
flag_keys_path: String,
|
flag_keys_path: String,
|
||||||
flag_archive: bool,
|
flag_archive: bool,
|
||||||
flag_no_bootstrap: bool,
|
flag_no_bootstrap: bool,
|
||||||
flag_listen_address: String,
|
flag_listen_address: String,
|
||||||
flag_public_address: Option<String>,
|
flag_public_address: Option<String>,
|
||||||
flag_address: Option<String>,
|
flag_address: Option<String>,
|
||||||
flag_peers: u32,
|
flag_peers: usize,
|
||||||
flag_no_discovery: bool,
|
flag_no_discovery: bool,
|
||||||
flag_no_upnp: bool,
|
flag_no_upnp: bool,
|
||||||
flag_node_key: Option<String>,
|
flag_node_key: Option<String>,
|
||||||
@ -116,8 +153,15 @@ struct Args {
|
|||||||
flag_cache_max_size: usize,
|
flag_cache_max_size: usize,
|
||||||
flag_queue_max_size: usize,
|
flag_queue_max_size: usize,
|
||||||
flag_jsonrpc: bool,
|
flag_jsonrpc: bool,
|
||||||
flag_jsonrpc_url: String,
|
flag_jsonrpc_addr: String,
|
||||||
|
flag_jsonrpc_port: u16,
|
||||||
flag_jsonrpc_cors: String,
|
flag_jsonrpc_cors: String,
|
||||||
|
flag_jsonrpc_apis: String,
|
||||||
|
flag_rpc: bool,
|
||||||
|
flag_rpcaddr: Option<String>,
|
||||||
|
flag_rpcport: Option<u16>,
|
||||||
|
flag_rpccorsdomain: Option<String>,
|
||||||
|
flag_rpcapi: Option<String>,
|
||||||
flag_logging: Option<String>,
|
flag_logging: Option<String>,
|
||||||
flag_version: bool,
|
flag_version: bool,
|
||||||
flag_author: String,
|
flag_author: String,
|
||||||
@ -151,19 +195,29 @@ fn setup_log(init: &Option<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, url: &str, cors_domain: &str) {
|
fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, url: &str, cors_domain: &str, apis: Vec<&str>) -> Option<Arc<PanicHandler>> {
|
||||||
use rpc::v1::*;
|
use rpc::v1::*;
|
||||||
|
|
||||||
let mut server = rpc::HttpServer::new(1);
|
let server = rpc::RpcServer::new();
|
||||||
server.add_delegate(Web3Client::new().to_delegate());
|
for api in apis.into_iter() {
|
||||||
|
match api {
|
||||||
|
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
|
||||||
|
"net" => server.add_delegate(NetClient::new(&sync).to_delegate()),
|
||||||
|
"eth" => {
|
||||||
server.add_delegate(EthClient::new(&client, &sync).to_delegate());
|
server.add_delegate(EthClient::new(&client, &sync).to_delegate());
|
||||||
server.add_delegate(EthFilterClient::new(&client).to_delegate());
|
server.add_delegate(EthFilterClient::new(&client).to_delegate());
|
||||||
server.add_delegate(NetClient::new(&sync).to_delegate());
|
}
|
||||||
server.start_async(url, cors_domain);
|
_ => {
|
||||||
|
die!("{}: Invalid API name to be enabled.", api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(server.start_http(url, cors_domain, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "rpc"))]
|
#[cfg(not(feature = "rpc"))]
|
||||||
fn setup_rpc_server(_client: Arc<Client>, _sync: Arc<EthSync>, _url: &str) {
|
fn setup_rpc_server(_client: Arc<Client>, _sync: Arc<EthSync>, _url: &str) -> Option<Arc<PanicHandler>> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_version() {
|
fn print_version() {
|
||||||
@ -179,16 +233,6 @@ By Wood/Paronyan/Kotewicz/Drwięga/Volf.\
|
|||||||
", version());
|
", version());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn die_with_message(msg: &str) -> ! {
|
|
||||||
println!("ERROR: {}", msg);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! die {
|
|
||||||
($($arg:tt)*) => (die_with_message(&format!("{}", format_args!($($arg)*))));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Configuration {
|
struct Configuration {
|
||||||
args: Args
|
args: Args
|
||||||
}
|
}
|
||||||
@ -201,7 +245,7 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn path(&self) -> String {
|
fn path(&self) -> String {
|
||||||
self.args.flag_db_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
self.args.flag_datadir.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn author(&self) -> Address {
|
fn author(&self) -> Address {
|
||||||
@ -221,8 +265,11 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spec(&self) -> Spec {
|
fn spec(&self) -> Spec {
|
||||||
|
if self.args.flag_testnet {
|
||||||
|
return ethereum::new_morden();
|
||||||
|
}
|
||||||
match self.args.flag_chain.as_ref() {
|
match self.args.flag_chain.as_ref() {
|
||||||
"frontier" | "mainnet" => ethereum::new_frontier(),
|
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
|
||||||
"morden" | "testnet" => ethereum::new_morden(),
|
"morden" | "testnet" => ethereum::new_morden(),
|
||||||
"olympic" => ethereum::new_olympic(),
|
"olympic" => ethereum::new_olympic(),
|
||||||
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| die!("{}: Couldn't read chain specification file. Sure it exists?", f)).as_ref()),
|
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| die!("{}: Couldn't read chain specification file. Sure it exists?", f)).as_ref()),
|
||||||
@ -246,7 +293,7 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(useless_format))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(useless_format))]
|
||||||
fn net_addresses(&self) -> (Option<SocketAddr>, Option<SocketAddr>) {
|
fn net_addresses(&self) -> (Option<SocketAddr>, Option<SocketAddr>) {
|
||||||
let mut listen_address = None;
|
let mut listen_address = None;
|
||||||
let mut public_address = None;
|
let mut public_address = None;
|
||||||
@ -276,7 +323,7 @@ impl Configuration {
|
|||||||
ret.public_address = public;
|
ret.public_address = public;
|
||||||
ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
|
ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
|
||||||
ret.discovery_enabled = !self.args.flag_no_discovery;
|
ret.discovery_enabled = !self.args.flag_no_discovery;
|
||||||
ret.ideal_peers = self.args.flag_peers;
|
ret.ideal_peers = self.args.flag_peers as u32;
|
||||||
let mut net_path = PathBuf::from(&self.path());
|
let mut net_path = PathBuf::from(&self.path());
|
||||||
net_path.push("network");
|
net_path.push("network");
|
||||||
ret.config_path = Some(net_path.to_str().unwrap().to_owned());
|
ret.config_path = Some(net_path.to_str().unwrap().to_owned());
|
||||||
@ -295,10 +342,44 @@ impl Configuration {
|
|||||||
.start()
|
.start()
|
||||||
.unwrap_or_else(|e| die!("Couldn't daemonize; {}", e));
|
.unwrap_or_else(|e| die!("Couldn't daemonize; {}", e));
|
||||||
}
|
}
|
||||||
|
if self.args.cmd_account {
|
||||||
|
self.execute_account_cli();
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.execute_client();
|
self.execute_client();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execute_account_cli(&self) {
|
||||||
|
use util::keys::store::SecretStore;
|
||||||
|
use rpassword::read_password;
|
||||||
|
let mut secret_store = SecretStore::new();
|
||||||
|
if self.args.cmd_new {
|
||||||
|
println!("Please note that password is NOT RECOVERABLE.");
|
||||||
|
println!("Type password: ");
|
||||||
|
let password = read_password().unwrap();
|
||||||
|
println!("Repeat password: ");
|
||||||
|
let password_repeat = read_password().unwrap();
|
||||||
|
if password != password_repeat {
|
||||||
|
println!("Passwords do not match!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println!("New account address:");
|
||||||
|
let new_address = secret_store.new_account(&password).unwrap();
|
||||||
|
println!("{:?}", new_address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.args.cmd_list {
|
||||||
|
println!("Known addresses:");
|
||||||
|
for &(addr, _) in secret_store.accounts().unwrap().iter() {
|
||||||
|
println!("{:?}", addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_client(&self) {
|
fn execute_client(&self) {
|
||||||
|
// Setup panic handler
|
||||||
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
|
|
||||||
// Setup logging
|
// Setup logging
|
||||||
setup_log(&self.args.flag_logging);
|
setup_log(&self.args.flag_logging);
|
||||||
// Raise fdlimit
|
// Raise fdlimit
|
||||||
@ -307,15 +388,25 @@ impl Configuration {
|
|||||||
let spec = self.spec();
|
let spec = self.spec();
|
||||||
let net_settings = self.net_settings(&spec);
|
let net_settings = self.net_settings(&spec);
|
||||||
let mut sync_config = SyncConfig::default();
|
let mut sync_config = SyncConfig::default();
|
||||||
sync_config.network_id = spec.network_id();
|
sync_config.network_id = self.args.flag_networkid.as_ref().map(|id| U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --networkid", id))).unwrap_or(spec.network_id());
|
||||||
|
|
||||||
// Build client
|
// Build client
|
||||||
let mut client_config = ClientConfig::default();
|
let mut client_config = ClientConfig::default();
|
||||||
|
match self.args.flag_cache {
|
||||||
|
Some(mb) => {
|
||||||
|
client_config.blockchain.max_cache_size = mb * 1024 * 1024;
|
||||||
|
client_config.blockchain.pref_cache_size = client_config.blockchain.max_cache_size / 2;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
|
client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
|
||||||
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
client_config.prefer_journal = !self.args.flag_archive;
|
client_config.prefer_journal = !self.args.flag_archive;
|
||||||
|
client_config.name = self.args.flag_identity.clone();
|
||||||
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
||||||
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
||||||
|
panic_handler.forward_from(&service);
|
||||||
let client = service.client().clone();
|
let client = service.client().clone();
|
||||||
client.set_author(self.author());
|
client.set_author(self.author());
|
||||||
client.set_extra_data(self.extra_data());
|
client.set_extra_data(self.extra_data());
|
||||||
@ -324,32 +415,45 @@ impl Configuration {
|
|||||||
let sync = EthSync::register(service.network(), sync_config, client);
|
let sync = EthSync::register(service.network(), sync_config, client);
|
||||||
|
|
||||||
// Setup rpc
|
// Setup rpc
|
||||||
if self.args.flag_jsonrpc {
|
if self.args.flag_jsonrpc || self.args.flag_rpc {
|
||||||
setup_rpc_server(service.client(), sync.clone(), &self.args.flag_jsonrpc_url, &self.args.flag_jsonrpc_cors);
|
let url = format!("{}:{}",
|
||||||
SocketAddr::from_str(&self.args.flag_jsonrpc_url).unwrap_or_else(|_|die!("{}: Invalid JSONRPC listen address given with --jsonrpc-url. Should be of the form 'IP:port'.", self.args.flag_jsonrpc_url));
|
self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_addr),
|
||||||
|
self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port)
|
||||||
|
);
|
||||||
|
SocketAddr::from_str(&url).unwrap_or_else(|_|die!("{}: Invalid JSONRPC listen host/port given.", url));
|
||||||
|
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
||||||
|
// TODO: use this as the API list.
|
||||||
|
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
||||||
|
let server_handler = setup_rpc_server(service.client(), sync.clone(), &url, cors, apis.split(",").collect());
|
||||||
|
if let Some(handler) = server_handler {
|
||||||
|
panic_handler.forward_from(handler.deref());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register IO handler
|
// Register IO handler
|
||||||
let io_handler = Arc::new(ClientIoHandler {
|
let io_handler = Arc::new(ClientIoHandler {
|
||||||
client: service.client(),
|
client: service.client(),
|
||||||
info: Default::default(),
|
info: Default::default(),
|
||||||
sync: sync
|
sync: sync.clone(),
|
||||||
});
|
});
|
||||||
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
service.io().register_handler(io_handler).expect("Error registering IO handler");
|
||||||
|
|
||||||
// Handle exit
|
// Handle exit
|
||||||
wait_for_exit(&service);
|
wait_for_exit(panic_handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_for_exit(client_service: &ClientService) {
|
fn wait_for_exit(panic_handler: Arc<PanicHandler>) {
|
||||||
let exit = Arc::new(Condvar::new());
|
let exit = Arc::new(Condvar::new());
|
||||||
|
|
||||||
// Handle possible exits
|
// Handle possible exits
|
||||||
let e = exit.clone();
|
let e = exit.clone();
|
||||||
CtrlC::set_handler(move || { e.notify_all(); });
|
CtrlC::set_handler(move || { e.notify_all(); });
|
||||||
|
|
||||||
|
// Handle panics
|
||||||
let e = exit.clone();
|
let e = exit.clone();
|
||||||
client_service.on_panic(move |_reason| { e.notify_all(); });
|
panic_handler.on_panic(move |_reason| { e.notify_all(); });
|
||||||
|
|
||||||
// Wait for signal
|
// Wait for signal
|
||||||
let mutex = Mutex::new(());
|
let mutex = Mutex::new(());
|
||||||
@ -395,7 +499,7 @@ impl Informant {
|
|||||||
let sync_info = sync.status();
|
let sync_info = sync.status();
|
||||||
|
|
||||||
if let (_, _, &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) {
|
if let (_, _, &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) {
|
||||||
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} chain, {} queue, {} sync ]",
|
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]",
|
||||||
chain_info.best_block_number,
|
chain_info.best_block_number,
|
||||||
chain_info.best_block_hash,
|
chain_info.best_block_hash,
|
||||||
(report.blocks_imported - last_report.blocks_imported) / dur,
|
(report.blocks_imported - last_report.blocks_imported) / dur,
|
||||||
@ -408,6 +512,7 @@ impl Informant {
|
|||||||
queue_info.unverified_queue_size,
|
queue_info.unverified_queue_size,
|
||||||
queue_info.verified_queue_size,
|
queue_info.verified_queue_size,
|
||||||
|
|
||||||
|
Informant::format_bytes(report.state_db_mem),
|
||||||
Informant::format_bytes(cache_info.total()),
|
Informant::format_bytes(cache_info.total()),
|
||||||
Informant::format_bytes(queue_info.mem_used),
|
Informant::format_bytes(queue_info.mem_used),
|
||||||
Informant::format_bytes(sync_info.mem_used),
|
Informant::format_bytes(sync_info.mem_used),
|
||||||
|
@ -12,8 +12,8 @@ build = "build.rs"
|
|||||||
log = "0.3"
|
log = "0.3"
|
||||||
serde = "0.7.0"
|
serde = "0.7.0"
|
||||||
serde_json = "0.7.0"
|
serde_json = "0.7.0"
|
||||||
jsonrpc-core = "1.2"
|
jsonrpc-core = "2.0"
|
||||||
jsonrpc-http-server = "2.1"
|
jsonrpc-http-server = "3.0"
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
ethash = { path = "../ethash" }
|
ethash = { path = "../ethash" }
|
||||||
@ -26,8 +26,9 @@ serde_macros = { version = "0.7.0", optional = true }
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_codegen = { version = "0.7.0", optional = true }
|
serde_codegen = { version = "0.7.0", optional = true }
|
||||||
syntex = "0.29.0"
|
syntex = "0.29.0"
|
||||||
|
rustc_version = "0.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["serde_codegen"]
|
default = ["serde_codegen"]
|
||||||
nightly = ["serde_macros"]
|
nightly = ["serde_macros"]
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev"]
|
dev = ["ethcore/dev", "ethcore-util/dev", "ethsync/dev"]
|
||||||
|
23
rpc/build.rs
23
rpc/build.rs
@ -1,3 +1,23 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate rustc_version;
|
||||||
|
|
||||||
|
use rustc_version::{version_meta, Channel};
|
||||||
|
|
||||||
#[cfg(not(feature = "serde_macros"))]
|
#[cfg(not(feature = "serde_macros"))]
|
||||||
mod inner {
|
mod inner {
|
||||||
extern crate syntex;
|
extern crate syntex;
|
||||||
@ -26,4 +46,7 @@ mod inner {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
inner::main();
|
inner::main();
|
||||||
|
if let Channel::Nightly = version_meta().channel {
|
||||||
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,33 +29,43 @@ extern crate ethcore;
|
|||||||
extern crate ethsync;
|
extern crate ethsync;
|
||||||
extern crate transient_hashmap;
|
extern crate transient_hashmap;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use util::panics::PanicHandler;
|
||||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
|
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
|
|
||||||
/// Http server.
|
/// Http server.
|
||||||
pub struct HttpServer {
|
pub struct RpcServer {
|
||||||
handler: IoHandler,
|
handler: Arc<IoHandler>,
|
||||||
threads: usize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpServer {
|
impl RpcServer {
|
||||||
/// Construct new http server object with given number of threads.
|
/// Construct new http server object with given number of threads.
|
||||||
pub fn new(threads: usize) -> HttpServer {
|
pub fn new() -> RpcServer {
|
||||||
HttpServer {
|
RpcServer {
|
||||||
handler: IoHandler::new(),
|
handler: Arc::new(IoHandler::new()),
|
||||||
threads: threads
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add io delegate.
|
/// Add io delegate.
|
||||||
pub fn add_delegate<D>(&mut self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
pub fn add_delegate<D>(&self, delegate: IoDelegate<D>) where D: Send + Sync + 'static {
|
||||||
self.handler.add_delegate(delegate);
|
self.handler.add_delegate(delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start server asynchronously in new thread
|
/// Start server asynchronously in new thread and returns panic handler.
|
||||||
pub fn start_async(self, addr: &str, cors_domain: &str) {
|
pub fn start_http(&self, addr: &str, cors_domain: &str, threads: usize) -> Arc<PanicHandler> {
|
||||||
let server = jsonrpc_http_server::Server::new(self.handler, self.threads);
|
let addr = addr.to_owned();
|
||||||
server.start_async(addr, jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain.to_owned()))
|
let cors_domain = cors_domain.to_owned();
|
||||||
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
|
let ph = panic_handler.clone();
|
||||||
|
let server = jsonrpc_http_server::Server::new(self.handler.clone());
|
||||||
|
thread::Builder::new().name("jsonrpc_http".to_string()).spawn(move || {
|
||||||
|
ph.catch_panic(move || {
|
||||||
|
server.start(addr.as_ref(), jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain), threads);
|
||||||
|
}).unwrap()
|
||||||
|
}).expect("Error while creating jsonrpc http thread");
|
||||||
|
panic_handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ impl<F, T> PollManager<F, T> where T: Timer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns number of block when last poll happend.
|
/// Returns number of block when last poll happend.
|
||||||
pub fn get_poll_info(&mut self, id: &PollId) -> Option<&PollInfo<F>> {
|
pub fn poll_info(&mut self, id: &PollId) -> Option<&PollInfo<F>> {
|
||||||
self.polls.prune();
|
self.polls.prune();
|
||||||
self.polls.get(id)
|
self.polls.get(id)
|
||||||
}
|
}
|
||||||
@ -124,21 +124,21 @@ mod tests {
|
|||||||
|
|
||||||
*time.borrow_mut() = 10;
|
*time.borrow_mut() = 10;
|
||||||
indexer.update_poll(&0, 21);
|
indexer.update_poll(&0, 21);
|
||||||
assert_eq!(indexer.get_poll_info(&0).unwrap().filter, false);
|
assert_eq!(indexer.poll_info(&0).unwrap().filter, false);
|
||||||
assert_eq!(indexer.get_poll_info(&0).unwrap().block_number, 21);
|
assert_eq!(indexer.poll_info(&0).unwrap().block_number, 21);
|
||||||
|
|
||||||
*time.borrow_mut() = 30;
|
*time.borrow_mut() = 30;
|
||||||
indexer.update_poll(&1, 23);
|
indexer.update_poll(&1, 23);
|
||||||
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
|
assert_eq!(indexer.poll_info(&1).unwrap().filter, true);
|
||||||
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);
|
assert_eq!(indexer.poll_info(&1).unwrap().block_number, 23);
|
||||||
|
|
||||||
*time.borrow_mut() = 75;
|
*time.borrow_mut() = 75;
|
||||||
indexer.update_poll(&0, 30);
|
indexer.update_poll(&0, 30);
|
||||||
assert!(indexer.get_poll_info(&0).is_none());
|
assert!(indexer.poll_info(&0).is_none());
|
||||||
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
|
assert_eq!(indexer.poll_info(&1).unwrap().filter, true);
|
||||||
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);
|
assert_eq!(indexer.poll_info(&1).unwrap().block_number, 23);
|
||||||
|
|
||||||
indexer.remove_poll(&1);
|
indexer.remove_poll(&1);
|
||||||
assert!(indexer.get_poll_info(&1).is_none());
|
assert!(indexer.poll_info(&1).is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! Eth rpc implementation.
|
//! Eth rpc implementation.
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Weak, Mutex, RwLock};
|
use std::sync::{Arc, Weak, Mutex, RwLock};
|
||||||
use ethsync::{EthSync, SyncState};
|
use ethsync::{SyncStatusProvider, SyncState};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use util::sha3::*;
|
use util::sha3::*;
|
||||||
@ -25,7 +25,6 @@ use util::rlp::encode;
|
|||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
use ethcore::block::{IsBlock};
|
use ethcore::block::{IsBlock};
|
||||||
use ethcore::views::*;
|
use ethcore::views::*;
|
||||||
//#[macro_use] extern crate log;
|
|
||||||
use ethcore::ethereum::Ethash;
|
use ethcore::ethereum::Ethash;
|
||||||
use ethcore::ethereum::denominations::shannon;
|
use ethcore::ethereum::denominations::shannon;
|
||||||
use v1::traits::{Eth, EthFilter};
|
use v1::traits::{Eth, EthFilter};
|
||||||
@ -33,15 +32,15 @@ use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncIn
|
|||||||
use v1::helpers::{PollFilter, PollManager};
|
use v1::helpers::{PollFilter, PollManager};
|
||||||
|
|
||||||
/// Eth rpc implementation.
|
/// Eth rpc implementation.
|
||||||
pub struct EthClient {
|
pub struct EthClient<C, S> where C: BlockChainClient, S: SyncStatusProvider {
|
||||||
client: Weak<Client>,
|
client: Weak<C>,
|
||||||
sync: Weak<EthSync>,
|
sync: Weak<S>,
|
||||||
hashrates: RwLock<HashMap<H256, u64>>,
|
hashrates: RwLock<HashMap<H256, u64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthClient {
|
impl<C, S> EthClient<C, S> where C: BlockChainClient, S: SyncStatusProvider {
|
||||||
/// Creates new EthClient.
|
/// Creates new EthClient.
|
||||||
pub fn new(client: &Arc<Client>, sync: &Arc<EthSync>) -> Self {
|
pub fn new(client: &Arc<C>, sync: &Arc<S>) -> Self {
|
||||||
EthClient {
|
EthClient {
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
sync: Arc::downgrade(sync),
|
sync: Arc::downgrade(sync),
|
||||||
@ -95,7 +94,7 @@ impl EthClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eth for EthClient {
|
impl<C, S> Eth for EthClient<C, S> where C: BlockChainClient + 'static, S: SyncStatusProvider + 'static {
|
||||||
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => to_value(&U256::from(take_weak!(self.sync).status().protocol_version)),
|
Params::None => to_value(&U256::from(take_weak!(self.sync).status().protocol_version)),
|
||||||
@ -256,14 +255,14 @@ impl Eth for EthClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Eth filter rpc implementation.
|
/// Eth filter rpc implementation.
|
||||||
pub struct EthFilterClient {
|
pub struct EthFilterClient<C> where C: BlockChainClient {
|
||||||
client: Weak<Client>,
|
client: Weak<C>,
|
||||||
polls: Mutex<PollManager<PollFilter>>,
|
polls: Mutex<PollManager<PollFilter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthFilterClient {
|
impl<C> EthFilterClient<C> where C: BlockChainClient {
|
||||||
/// Creates new Eth filter client.
|
/// Creates new Eth filter client.
|
||||||
pub fn new(client: &Arc<Client>) -> Self {
|
pub fn new(client: &Arc<C>) -> Self {
|
||||||
EthFilterClient {
|
EthFilterClient {
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
polls: Mutex::new(PollManager::new())
|
polls: Mutex::new(PollManager::new())
|
||||||
@ -271,7 +270,7 @@ impl EthFilterClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthFilter for EthFilterClient {
|
impl<C> EthFilter for EthFilterClient<C> where C: BlockChainClient + 'static {
|
||||||
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(Filter,)>(params)
|
from_params::<(Filter,)>(params)
|
||||||
.and_then(|(filter,)| {
|
.and_then(|(filter,)| {
|
||||||
@ -307,12 +306,13 @@ impl EthFilter for EthFilterClient {
|
|||||||
let client = take_weak!(self.client);
|
let client = take_weak!(self.client);
|
||||||
from_params::<(Index,)>(params)
|
from_params::<(Index,)>(params)
|
||||||
.and_then(|(index,)| {
|
.and_then(|(index,)| {
|
||||||
let info = self.polls.lock().unwrap().get_poll_info(&index.value()).cloned();
|
let info = self.polls.lock().unwrap().poll_info(&index.value()).cloned();
|
||||||
match info {
|
match info {
|
||||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||||
Some(info) => match info.filter {
|
Some(info) => match info.filter {
|
||||||
PollFilter::Block => {
|
PollFilter::Block => {
|
||||||
let current_number = client.chain_info().best_block_number;
|
// + 1, cause we want to return hashes including current block hash.
|
||||||
|
let current_number = client.chain_info().best_block_number + 1;
|
||||||
let hashes = (info.block_number..current_number).into_iter()
|
let hashes = (info.block_number..current_number).into_iter()
|
||||||
.map(BlockId::Number)
|
.map(BlockId::Number)
|
||||||
.filter_map(|id| client.block_hash(id))
|
.filter_map(|id| client.block_hash(id))
|
||||||
|
@ -17,24 +17,24 @@
|
|||||||
//! Net rpc implementation.
|
//! Net rpc implementation.
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use ethsync::EthSync;
|
use ethsync::SyncStatusProvider;
|
||||||
use v1::traits::Net;
|
use v1::traits::Net;
|
||||||
|
|
||||||
/// Net rpc implementation.
|
/// Net rpc implementation.
|
||||||
pub struct NetClient {
|
pub struct NetClient<S> where S: SyncStatusProvider {
|
||||||
sync: Weak<EthSync>
|
sync: Weak<S>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetClient {
|
impl<S> NetClient<S> where S: SyncStatusProvider {
|
||||||
/// Creates new NetClient.
|
/// Creates new NetClient.
|
||||||
pub fn new(sync: &Arc<EthSync>) -> Self {
|
pub fn new(sync: &Arc<S>) -> Self {
|
||||||
NetClient {
|
NetClient {
|
||||||
sync: Arc::downgrade(sync)
|
sync: Arc::downgrade(sync)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Net for NetClient {
|
impl<S> Net for NetClient<S> where S: SyncStatusProvider + 'static {
|
||||||
fn version(&self, _: Params) -> Result<Value, Error> {
|
fn version(&self, _: Params) -> Result<Value, Error> {
|
||||||
Ok(Value::U64(take_weak!(self.sync).status().protocol_version as u64))
|
Ok(Value::U64(take_weak!(self.sync).status().protocol_version as u64))
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,13 @@ name = "ethsync"
|
|||||||
version = "0.9.99"
|
version = "0.9.99"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Ethcore <admin@ethcore.io"]
|
authors = ["Ethcore <admin@ethcore.io"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rustc_version = "0.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
@ -17,7 +21,8 @@ time = "0.1.34"
|
|||||||
rand = "0.3.13"
|
rand = "0.3.13"
|
||||||
heapsize = "0.3"
|
heapsize = "0.3"
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
|
rayon = "0.3.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev"]
|
dev = ["ethcore/dev", "ethcore-util/dev"]
|
||||||
|
25
sync/build.rs
Normal file
25
sync/build.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate rustc_version;
|
||||||
|
|
||||||
|
use rustc_version::{version_meta, Channel};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Channel::Nightly = version_meta().channel {
|
||||||
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
}
|
||||||
|
}
|
@ -30,16 +30,20 @@
|
|||||||
///
|
///
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
|
use rayon::prelude::*;
|
||||||
use std::mem::{replace};
|
use std::mem::{replace};
|
||||||
use ethcore::views::{HeaderView};
|
use ethcore::views::{HeaderView, BlockView};
|
||||||
use ethcore::header::{BlockNumber, Header as BlockHeader};
|
use ethcore::header::{BlockNumber, Header as BlockHeader};
|
||||||
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo};
|
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo};
|
||||||
use range_collection::{RangeCollection, ToUsize, FromUsize};
|
use range_collection::{RangeCollection, ToUsize, FromUsize};
|
||||||
use ethcore::error::*;
|
use ethcore::error::*;
|
||||||
use ethcore::block::Block;
|
use ethcore::block::Block;
|
||||||
|
use ethcore::transaction::SignedTransaction;
|
||||||
use io::SyncIo;
|
use io::SyncIo;
|
||||||
|
use transaction_queue::TransactionQueue;
|
||||||
use time;
|
use time;
|
||||||
use super::SyncConfig;
|
use super::SyncConfig;
|
||||||
|
use ethcore;
|
||||||
|
|
||||||
known_heap_size!(0, PeerInfo, Header, HeaderId);
|
known_heap_size!(0, PeerInfo, Header, HeaderId);
|
||||||
|
|
||||||
@ -204,11 +208,13 @@ pub struct ChainSync {
|
|||||||
/// True if common block for our and remote chain has been found
|
/// True if common block for our and remote chain has been found
|
||||||
have_common_block: bool,
|
have_common_block: bool,
|
||||||
/// Last propagated block number
|
/// Last propagated block number
|
||||||
last_send_block_number: BlockNumber,
|
last_sent_block_number: BlockNumber,
|
||||||
/// Max blocks to download ahead
|
/// Max blocks to download ahead
|
||||||
max_download_ahead_blocks: usize,
|
max_download_ahead_blocks: usize,
|
||||||
/// Network ID
|
/// Network ID
|
||||||
network_id: U256,
|
network_id: U256,
|
||||||
|
/// Transactions Queue
|
||||||
|
transaction_queue: Mutex<TransactionQueue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
||||||
@ -231,9 +237,10 @@ impl ChainSync {
|
|||||||
last_imported_hash: None,
|
last_imported_hash: None,
|
||||||
syncing_difficulty: U256::from(0u64),
|
syncing_difficulty: U256::from(0u64),
|
||||||
have_common_block: false,
|
have_common_block: false,
|
||||||
last_send_block_number: 0,
|
last_sent_block_number: 0,
|
||||||
max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
|
max_download_ahead_blocks: max(MAX_HEADERS_TO_REQUEST, config.max_download_ahead_blocks),
|
||||||
network_id: config.network_id,
|
network_id: config.network_id,
|
||||||
|
transaction_queue: Mutex::new(TransactionQueue::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +275,7 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(for_kv_map))] // Because it's not possible to get `values_mut()`
|
#[cfg_attr(all(nightly, feature="dev"), allow(for_kv_map))] // Because it's not possible to get `values_mut()`
|
||||||
/// Rest sync. Clear all downloaded data but keep the queue
|
/// Rest sync. Clear all downloaded data but keep the queue
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.downloading_headers.clear();
|
self.downloading_headers.clear();
|
||||||
@ -292,6 +299,7 @@ impl ChainSync {
|
|||||||
self.starting_block = 0;
|
self.starting_block = 0;
|
||||||
self.highest_block = None;
|
self.highest_block = None;
|
||||||
self.have_common_block = false;
|
self.have_common_block = false;
|
||||||
|
self.transaction_queue.lock().unwrap().clear();
|
||||||
self.starting_block = io.chain().chain_info().best_block_number;
|
self.starting_block = io.chain().chain_info().best_block_number;
|
||||||
self.state = SyncState::NotSynced;
|
self.state = SyncState::NotSynced;
|
||||||
}
|
}
|
||||||
@ -335,7 +343,7 @@ impl ChainSync {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
|
||||||
/// Called by peer once it has new block headers during sync
|
/// Called by peer once it has new block headers during sync
|
||||||
fn on_peer_block_headers(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
fn on_peer_block_headers(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
||||||
self.reset_peer_asking(peer_id, PeerAsking::BlockHeaders);
|
self.reset_peer_asking(peer_id, PeerAsking::BlockHeaders);
|
||||||
@ -462,6 +470,7 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Called by peer once it has new block bodies
|
/// Called by peer once it has new block bodies
|
||||||
|
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
|
||||||
fn on_peer_new_block(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
fn on_peer_new_block(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
||||||
let block_rlp = try!(r.at(0));
|
let block_rlp = try!(r.at(0));
|
||||||
let header_rlp = try!(block_rlp.at(0));
|
let header_rlp = try!(block_rlp.at(0));
|
||||||
@ -575,7 +584,7 @@ impl ChainSync {
|
|||||||
pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) {
|
pub fn on_peer_connected(&mut self, io: &mut SyncIo, peer: PeerId) {
|
||||||
trace!(target: "sync", "== Connected {}", peer);
|
trace!(target: "sync", "== Connected {}", peer);
|
||||||
if let Err(e) = self.send_status(io) {
|
if let Err(e) = self.send_status(io) {
|
||||||
trace!(target:"sync", "Error sending status request: {:?}", e);
|
warn!(target:"sync", "Error sending status request: {:?}", e);
|
||||||
io.disable_peer(peer);
|
io.disable_peer(peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -843,8 +852,8 @@ impl ChainSync {
|
|||||||
self.downloading_bodies.remove(&n);
|
self.downloading_bodies.remove(&n);
|
||||||
self.downloading_headers.remove(&n);
|
self.downloading_headers.remove(&n);
|
||||||
}
|
}
|
||||||
self.headers.remove_tail(&start);
|
self.headers.remove_from(&start);
|
||||||
self.bodies.remove_tail(&start);
|
self.bodies.remove_from(&start);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request headers from a peer by block hash
|
/// Request headers from a peer by block hash
|
||||||
@ -900,9 +909,8 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
match sync.send(peer_id, packet_id, packet) {
|
match sync.send(peer_id, packet_id, packet) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!(target:"sync", "Error sending request: {:?}", e);
|
debug!(target:"sync", "Error sending request: {:?}", e);
|
||||||
sync.disable_peer(peer_id);
|
sync.disable_peer(peer_id);
|
||||||
self.on_peer_aborting(sync, peer_id);
|
|
||||||
}
|
}
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let mut peer = self.peers.get_mut(&peer_id).unwrap();
|
let mut peer = self.peers.get_mut(&peer_id).unwrap();
|
||||||
@ -915,13 +923,22 @@ impl ChainSync {
|
|||||||
/// Generic packet sender
|
/// Generic packet sender
|
||||||
fn send_packet(&mut self, sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) {
|
fn send_packet(&mut self, sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) {
|
||||||
if let Err(e) = sync.send(peer_id, packet_id, packet) {
|
if let Err(e) = sync.send(peer_id, packet_id, packet) {
|
||||||
warn!(target:"sync", "Error sending packet: {:?}", e);
|
debug!(target:"sync", "Error sending packet: {:?}", e);
|
||||||
sync.disable_peer(peer_id);
|
sync.disable_peer(peer_id);
|
||||||
self.on_peer_aborting(sync, peer_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Called when peer sends us new transactions
|
/// Called when peer sends us new transactions
|
||||||
fn on_peer_transactions(&mut self, _io: &mut SyncIo, _peer_id: PeerId, _r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
||||||
|
let chain = io.chain();
|
||||||
|
let item_count = r.item_count();
|
||||||
|
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
|
||||||
|
let fetch_latest_nonce = |a : &Address| chain.nonce(a);
|
||||||
|
|
||||||
|
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||||
|
for i in 0..item_count {
|
||||||
|
let tx: SignedTransaction = try!(r.val_at(i));
|
||||||
|
let _ = transaction_queue.add(tx, &fetch_latest_nonce);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1231,22 +1248,66 @@ impl ChainSync {
|
|||||||
sent
|
sent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn propagate_latest_blocks(&mut self, io: &mut SyncIo) {
|
||||||
|
let chain_info = io.chain().chain_info();
|
||||||
|
if (((chain_info.best_block_number as i64) - (self.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION {
|
||||||
|
let blocks = self.propagate_blocks(&chain_info, io);
|
||||||
|
let hashes = self.propagate_new_hashes(&chain_info, io);
|
||||||
|
if blocks != 0 || hashes != 0 {
|
||||||
|
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.last_sent_block_number = chain_info.best_block_number;
|
||||||
|
}
|
||||||
|
|
||||||
/// Maintain other peers. Send out any new blocks and transactions
|
/// Maintain other peers. Send out any new blocks and transactions
|
||||||
pub fn maintain_sync(&mut self, io: &mut SyncIo) {
|
pub fn maintain_sync(&mut self, io: &mut SyncIo) {
|
||||||
self.check_resume(io);
|
self.check_resume(io);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// should be called once chain has new block, triggers the latest block propagation
|
/// called when block is imported to chain, updates transactions queue and propagates the blocks
|
||||||
pub fn chain_blocks_verified(&mut self, io: &mut SyncIo) {
|
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, good: &[H256], bad: &[H256], _retracted: &[H256]) {
|
||||||
let chain = io.chain().chain_info();
|
fn fetch_transactions(chain: &BlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
|
||||||
if (((chain.best_block_number as i64) - (self.last_send_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION {
|
let block = chain
|
||||||
let blocks = self.propagate_blocks(&chain, io);
|
.block(BlockId::Hash(hash.clone()))
|
||||||
let hashes = self.propagate_new_hashes(&chain, io);
|
// Client should send message after commit to db and inserting to chain.
|
||||||
if blocks != 0 || hashes != 0 {
|
.expect("Expected in-chain blocks.");
|
||||||
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
|
let block = BlockView::new(&block);
|
||||||
|
block.transactions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
let chain = io.chain();
|
||||||
|
let good = good.par_iter().map(|h| fetch_transactions(chain, h));
|
||||||
|
let bad = bad.par_iter().map(|h| fetch_transactions(chain, h));
|
||||||
|
|
||||||
|
good.for_each(|txs| {
|
||||||
|
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||||
|
let hashes = txs.iter().map(|tx| tx.hash()).collect::<Vec<H256>>();
|
||||||
|
transaction_queue.remove_all(&hashes, |a| chain.nonce(a));
|
||||||
|
});
|
||||||
|
bad.for_each(|txs| {
|
||||||
|
// populate sender
|
||||||
|
for tx in &txs {
|
||||||
|
let _sender = tx.sender();
|
||||||
}
|
}
|
||||||
self.last_send_block_number = chain.best_block_number;
|
let mut transaction_queue = self.transaction_queue.lock().unwrap();
|
||||||
|
let _ = transaction_queue.add_all(txs, |a| chain.nonce(a));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagate latests blocks
|
||||||
|
self.propagate_latest_blocks(io);
|
||||||
|
// TODO [todr] propagate transactions?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add transaction to the transaction queue
|
||||||
|
pub fn insert_transaction<T>(&self, transaction: ethcore::transaction::SignedTransaction, fetch_nonce: &T)
|
||||||
|
where T: Fn(&Address) -> U256
|
||||||
|
{
|
||||||
|
let mut queue = self.transaction_queue.lock().unwrap();
|
||||||
|
queue.add(transaction, fetch_nonce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1388,7 +1449,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn finds_lagging_peers() {
|
fn finds_lagging_peers() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(100, false);
|
client.add_blocks(100, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(10));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(10));
|
||||||
let chain_info = client.chain_info();
|
let chain_info = client.chain_info();
|
||||||
@ -1402,7 +1463,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn calculates_tree_for_lagging_peer() {
|
fn calculates_tree_for_lagging_peer() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(15, false);
|
client.add_blocks(15, EachBlockWith::Uncle);
|
||||||
|
|
||||||
let start = client.block_hash_delta_minus(4);
|
let start = client.block_hash_delta_minus(4);
|
||||||
let end = client.block_hash_delta_minus(2);
|
let end = client.block_hash_delta_minus(2);
|
||||||
@ -1419,7 +1480,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn sends_new_hashes_to_lagging_peer() {
|
fn sends_new_hashes_to_lagging_peer() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(100, false);
|
client.add_blocks(100, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let chain_info = client.chain_info();
|
let chain_info = client.chain_info();
|
||||||
@ -1438,7 +1499,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn sends_latest_block_to_lagging_peer() {
|
fn sends_latest_block_to_lagging_peer() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(100, false);
|
client.add_blocks(100, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let chain_info = client.chain_info();
|
let chain_info = client.chain_info();
|
||||||
@ -1456,7 +1517,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn handles_peer_new_block_mallformed() {
|
fn handles_peer_new_block_mallformed() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(10, false);
|
client.add_blocks(10, EachBlockWith::Uncle);
|
||||||
|
|
||||||
let block_data = get_dummy_block(11, client.chain_info().best_block_hash);
|
let block_data = get_dummy_block(11, client.chain_info().best_block_hash);
|
||||||
|
|
||||||
@ -1474,7 +1535,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn handles_peer_new_block() {
|
fn handles_peer_new_block() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(10, false);
|
client.add_blocks(10, EachBlockWith::Uncle);
|
||||||
|
|
||||||
let block_data = get_dummy_blocks(11, client.chain_info().best_block_hash);
|
let block_data = get_dummy_blocks(11, client.chain_info().best_block_hash);
|
||||||
|
|
||||||
@ -1492,7 +1553,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn handles_peer_new_block_empty() {
|
fn handles_peer_new_block_empty() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(10, false);
|
client.add_blocks(10, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
@ -1508,7 +1569,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn handles_peer_new_hashes() {
|
fn handles_peer_new_hashes() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(10, false);
|
client.add_blocks(10, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
@ -1524,7 +1585,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn handles_peer_new_hashes_empty() {
|
fn handles_peer_new_hashes_empty() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(10, false);
|
client.add_blocks(10, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
@ -1542,7 +1603,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hashes_rlp_mutually_acceptable() {
|
fn hashes_rlp_mutually_acceptable() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(100, false);
|
client.add_blocks(100, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let chain_info = client.chain_info();
|
let chain_info = client.chain_info();
|
||||||
@ -1560,7 +1621,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn block_rlp_mutually_acceptable() {
|
fn block_rlp_mutually_acceptable() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(100, false);
|
client.add_blocks(100, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
let chain_info = client.chain_info();
|
let chain_info = client.chain_info();
|
||||||
@ -1573,10 +1634,37 @@ mod tests {
|
|||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_add_transactions_to_queue() {
|
||||||
|
// given
|
||||||
|
let mut client = TestBlockChainClient::new();
|
||||||
|
client.add_blocks(98, EachBlockWith::Uncle);
|
||||||
|
client.add_blocks(1, EachBlockWith::UncleAndTransaction);
|
||||||
|
client.add_blocks(1, EachBlockWith::Transaction);
|
||||||
|
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
|
||||||
|
|
||||||
|
let good_blocks = vec![client.block_hash_delta_minus(2)];
|
||||||
|
let retracted_blocks = vec![client.block_hash_delta_minus(1)];
|
||||||
|
|
||||||
|
let mut queue = VecDeque::new();
|
||||||
|
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
|
||||||
|
// when
|
||||||
|
sync.chain_new_blocks(&mut io, &[], &good_blocks, &[]);
|
||||||
|
assert_eq!(sync.transaction_queue.lock().unwrap().status().future, 0);
|
||||||
|
assert_eq!(sync.transaction_queue.lock().unwrap().status().pending, 1);
|
||||||
|
sync.chain_new_blocks(&mut io, &good_blocks, &retracted_blocks, &[]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let status = sync.transaction_queue.lock().unwrap().status();
|
||||||
|
assert_eq!(status.pending, 1);
|
||||||
|
assert_eq!(status.future, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn returns_requested_block_headers() {
|
fn returns_requested_block_headers() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(100, false);
|
client.add_blocks(100, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let io = TestIo::new(&mut client, &mut queue, None);
|
let io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
|
||||||
@ -1600,7 +1688,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn returns_requested_block_headers_reverse() {
|
fn returns_requested_block_headers_reverse() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
client.add_blocks(100, false);
|
client.add_blocks(100, EachBlockWith::Uncle);
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
let io = TestIo::new(&mut client, &mut queue, None);
|
let io = TestIo::new(&mut client, &mut queue, None);
|
||||||
|
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![cfg_attr(feature="dev", feature(plugin))]
|
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
|
||||||
#![cfg_attr(feature="dev", plugin(clippy))]
|
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
|
||||||
|
|
||||||
// Keeps consistency (all lines with `.clone()`) and helpful when changing ref to non-ref.
|
// Keeps consistency (all lines with `.clone()`) and helpful when changing ref to non-ref.
|
||||||
#![cfg_attr(feature="dev", allow(clone_on_copy))]
|
#![cfg_attr(all(nightly, feature="dev"), allow(clone_on_copy))]
|
||||||
|
|
||||||
//! Blockchain sync module
|
//! Blockchain sync module
|
||||||
//! Implements ethereum protocol version 63 as specified here:
|
//! Implements ethereum protocol version 63 as specified here:
|
||||||
@ -54,6 +54,7 @@ extern crate ethcore;
|
|||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
extern crate rayon;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate heapsize;
|
extern crate heapsize;
|
||||||
|
|
||||||
@ -70,8 +71,8 @@ use io::NetSyncIo;
|
|||||||
mod chain;
|
mod chain;
|
||||||
mod io;
|
mod io;
|
||||||
mod range_collection;
|
mod range_collection;
|
||||||
// TODO [todr] Made public to suppress dead code warnings
|
mod transaction_queue;
|
||||||
pub mod transaction_queue;
|
pub use transaction_queue::TransactionQueue;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@ -93,6 +94,12 @@ impl Default for SyncConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Current sync status
|
||||||
|
pub trait SyncStatusProvider: Send + Sync {
|
||||||
|
/// Get sync status
|
||||||
|
fn status(&self) -> SyncStatus;
|
||||||
|
}
|
||||||
|
|
||||||
/// Ethereum network protocol handler
|
/// Ethereum network protocol handler
|
||||||
pub struct EthSync {
|
pub struct EthSync {
|
||||||
/// Shared blockchain client. TODO: this should evetually become an IPC endpoint
|
/// Shared blockchain client. TODO: this should evetually become an IPC endpoint
|
||||||
@ -114,11 +121,6 @@ impl EthSync {
|
|||||||
sync
|
sync
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get sync status
|
|
||||||
pub fn status(&self) -> SyncStatus {
|
|
||||||
self.sync.read().unwrap().status()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stop sync
|
/// Stop sync
|
||||||
pub fn stop(&mut self, io: &mut NetworkContext<SyncMessage>) {
|
pub fn stop(&mut self, io: &mut NetworkContext<SyncMessage>) {
|
||||||
self.sync.write().unwrap().abort(&mut NetSyncIo::new(io, self.chain.deref()));
|
self.sync.write().unwrap().abort(&mut NetSyncIo::new(io, self.chain.deref()));
|
||||||
@ -128,6 +130,22 @@ impl EthSync {
|
|||||||
pub fn restart(&mut self, io: &mut NetworkContext<SyncMessage>) {
|
pub fn restart(&mut self, io: &mut NetworkContext<SyncMessage>) {
|
||||||
self.sync.write().unwrap().restart(&mut NetSyncIo::new(io, self.chain.deref()));
|
self.sync.write().unwrap().restart(&mut NetSyncIo::new(io, self.chain.deref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert transaction in transaction queue
|
||||||
|
pub fn insert_transaction(&self, transaction: ethcore::transaction::SignedTransaction) {
|
||||||
|
use util::numbers::*;
|
||||||
|
|
||||||
|
let nonce_fn = |a: &Address| self.chain.state().nonce(a) + U256::one();
|
||||||
|
let sync = self.sync.write().unwrap();
|
||||||
|
sync.insert_transaction(transaction, &nonce_fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyncStatusProvider for EthSync {
|
||||||
|
/// Get sync status
|
||||||
|
fn status(&self) -> SyncStatus {
|
||||||
|
self.sync.read().unwrap().status()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
||||||
@ -153,8 +171,12 @@ impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn message(&self, io: &NetworkContext<SyncMessage>, message: &SyncMessage) {
|
fn message(&self, io: &NetworkContext<SyncMessage>, message: &SyncMessage) {
|
||||||
if let SyncMessage::BlockVerified = *message {
|
match *message {
|
||||||
self.sync.write().unwrap().chain_blocks_verified(&mut NetSyncIo::new(io, self.chain.deref()));
|
SyncMessage::NewChainBlocks { ref good, ref bad, ref retracted } => {
|
||||||
|
let mut sync_io = NetSyncIo::new(io, self.chain.deref());
|
||||||
|
self.sync.write().unwrap().chain_new_blocks(&mut sync_io, good, bad, retracted);
|
||||||
|
},
|
||||||
|
_ => {/* Ignore other messages */},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ pub trait RangeCollection<K, V> {
|
|||||||
fn remove_head(&mut self, start: &K);
|
fn remove_head(&mut self, start: &K);
|
||||||
/// Remove all elements >= `start` in the range that contains `start`
|
/// Remove all elements >= `start` in the range that contains `start`
|
||||||
fn remove_tail(&mut self, start: &K);
|
fn remove_tail(&mut self, start: &K);
|
||||||
|
/// Remove all elements >= `start`
|
||||||
|
fn remove_from(&mut self, start: &K);
|
||||||
/// Remove all elements >= `tail`
|
/// Remove all elements >= `tail`
|
||||||
fn insert_item(&mut self, key: K, value: V);
|
fn insert_item(&mut self, key: K, value: V);
|
||||||
/// Get an iterator over ranges
|
/// Get an iterator over ranges
|
||||||
@ -137,6 +139,28 @@ impl<K, V> RangeCollection<K, V> for Vec<(K, Vec<V>)> where K: Ord + PartialEq +
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove the element and all following it.
|
||||||
|
fn remove_from(&mut self, key: &K) {
|
||||||
|
match self.binary_search_by(|&(k, _)| k.cmp(key).reverse()) {
|
||||||
|
Ok(index) => { self.drain(.. index + 1); },
|
||||||
|
Err(index) =>{
|
||||||
|
let mut empty = false;
|
||||||
|
match self.get_mut(index) {
|
||||||
|
Some(&mut (ref k, ref mut v)) if k <= key && (*k + FromUsize::from_usize(v.len())) > *key => {
|
||||||
|
v.truncate((*key - *k).to_usize());
|
||||||
|
empty = v.is_empty();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if empty {
|
||||||
|
self.drain(.. index + 1);
|
||||||
|
} else {
|
||||||
|
self.drain(.. index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove range elements up to key
|
/// Remove range elements up to key
|
||||||
fn remove_head(&mut self, key: &K) {
|
fn remove_head(&mut self, key: &K) {
|
||||||
if *key == FromUsize::from_usize(0) {
|
if *key == FromUsize::from_usize(0) {
|
||||||
@ -207,7 +231,7 @@ impl<K, V> RangeCollection<K, V> for Vec<(K, Vec<V>)> where K: Ord + PartialEq +
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
|
||||||
fn test_range() {
|
fn test_range() {
|
||||||
use std::cmp::{Ordering};
|
use std::cmp::{Ordering};
|
||||||
|
|
||||||
@ -272,5 +296,17 @@ fn test_range() {
|
|||||||
assert_eq!(r.range_iter().cmp(vec![(2, &['b'][..])]), Ordering::Equal);
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b'][..])]), Ordering::Equal);
|
||||||
r.remove_tail(&2);
|
r.remove_tail(&2);
|
||||||
assert_eq!(r.range_iter().next(), None);
|
assert_eq!(r.range_iter().next(), None);
|
||||||
|
|
||||||
|
let mut r = ranges.clone();
|
||||||
|
r.remove_from(&20);
|
||||||
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p', 'q', 'r'][..])]), Ordering::Equal);
|
||||||
|
r.remove_from(&17);
|
||||||
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..]), (16, &['p'][..])]), Ordering::Equal);
|
||||||
|
r.remove_from(&15);
|
||||||
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b', 'c', 'd'][..])]), Ordering::Equal);
|
||||||
|
r.remove_from(&3);
|
||||||
|
assert_eq!(r.range_iter().cmp(vec![(2, &['b'][..])]), Ordering::Equal);
|
||||||
|
r.remove_from(&2);
|
||||||
|
assert_eq!(r.range_iter().next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,8 @@ use super::helpers::*;
|
|||||||
fn two_peers() {
|
fn two_peers() {
|
||||||
::env_logger::init().ok();
|
::env_logger::init().ok();
|
||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
net.peer_mut(1).chain.add_blocks(1000, false);
|
net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||||
net.peer_mut(2).chain.add_blocks(1000, false);
|
net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||||
net.sync();
|
net.sync();
|
||||||
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
|
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
|
||||||
assert_eq!(net.peer(0).chain.blocks.read().unwrap().deref(), net.peer(1).chain.blocks.read().unwrap().deref());
|
assert_eq!(net.peer(0).chain.blocks.read().unwrap().deref(), net.peer(1).chain.blocks.read().unwrap().deref());
|
||||||
@ -35,8 +35,8 @@ fn two_peers() {
|
|||||||
fn status_after_sync() {
|
fn status_after_sync() {
|
||||||
::env_logger::init().ok();
|
::env_logger::init().ok();
|
||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
net.peer_mut(1).chain.add_blocks(1000, false);
|
net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||||
net.peer_mut(2).chain.add_blocks(1000, false);
|
net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||||
net.sync();
|
net.sync();
|
||||||
let status = net.peer(0).sync.status();
|
let status = net.peer(0).sync.status();
|
||||||
assert_eq!(status.state, SyncState::Idle);
|
assert_eq!(status.state, SyncState::Idle);
|
||||||
@ -45,8 +45,8 @@ fn status_after_sync() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn takes_few_steps() {
|
fn takes_few_steps() {
|
||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
net.peer_mut(1).chain.add_blocks(100, false);
|
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Uncle);
|
||||||
net.peer_mut(2).chain.add_blocks(100, false);
|
net.peer_mut(2).chain.add_blocks(100, EachBlockWith::Uncle);
|
||||||
let total_steps = net.sync();
|
let total_steps = net.sync();
|
||||||
assert!(total_steps < 7);
|
assert!(total_steps < 7);
|
||||||
}
|
}
|
||||||
@ -56,8 +56,9 @@ fn empty_blocks() {
|
|||||||
::env_logger::init().ok();
|
::env_logger::init().ok();
|
||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
for n in 0..200 {
|
for n in 0..200 {
|
||||||
net.peer_mut(1).chain.add_blocks(5, n % 2 == 0);
|
let with = if n % 2 == 0 { EachBlockWith::Nothing } else { EachBlockWith::Uncle };
|
||||||
net.peer_mut(2).chain.add_blocks(5, n % 2 == 0);
|
net.peer_mut(1).chain.add_blocks(5, with.clone());
|
||||||
|
net.peer_mut(2).chain.add_blocks(5, with);
|
||||||
}
|
}
|
||||||
net.sync();
|
net.sync();
|
||||||
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
|
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
|
||||||
@ -68,14 +69,14 @@ fn empty_blocks() {
|
|||||||
fn forked() {
|
fn forked() {
|
||||||
::env_logger::init().ok();
|
::env_logger::init().ok();
|
||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
net.peer_mut(0).chain.add_blocks(300, false);
|
net.peer_mut(0).chain.add_blocks(300, EachBlockWith::Uncle);
|
||||||
net.peer_mut(1).chain.add_blocks(300, false);
|
net.peer_mut(1).chain.add_blocks(300, EachBlockWith::Uncle);
|
||||||
net.peer_mut(2).chain.add_blocks(300, false);
|
net.peer_mut(2).chain.add_blocks(300, EachBlockWith::Uncle);
|
||||||
net.peer_mut(0).chain.add_blocks(100, true); //fork
|
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Nothing); //fork
|
||||||
net.peer_mut(1).chain.add_blocks(200, false);
|
net.peer_mut(1).chain.add_blocks(200, EachBlockWith::Uncle);
|
||||||
net.peer_mut(2).chain.add_blocks(200, false);
|
net.peer_mut(2).chain.add_blocks(200, EachBlockWith::Uncle);
|
||||||
net.peer_mut(1).chain.add_blocks(100, false); //fork between 1 and 2
|
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Uncle); //fork between 1 and 2
|
||||||
net.peer_mut(2).chain.add_blocks(10, true);
|
net.peer_mut(2).chain.add_blocks(10, EachBlockWith::Nothing);
|
||||||
// peer 1 has the best chain of 601 blocks
|
// peer 1 has the best chain of 601 blocks
|
||||||
let peer1_chain = net.peer(1).chain.numbers.read().unwrap().clone();
|
let peer1_chain = net.peer(1).chain.numbers.read().unwrap().clone();
|
||||||
net.sync();
|
net.sync();
|
||||||
@ -87,8 +88,8 @@ fn forked() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn restart() {
|
fn restart() {
|
||||||
let mut net = TestNet::new(3);
|
let mut net = TestNet::new(3);
|
||||||
net.peer_mut(1).chain.add_blocks(1000, false);
|
net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||||
net.peer_mut(2).chain.add_blocks(1000, false);
|
net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||||
|
|
||||||
net.sync_steps(8);
|
net.sync_steps(8);
|
||||||
|
|
||||||
@ -109,8 +110,8 @@ fn status_empty() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn status_packet() {
|
fn status_packet() {
|
||||||
let mut net = TestNet::new(2);
|
let mut net = TestNet::new(2);
|
||||||
net.peer_mut(0).chain.add_blocks(100, false);
|
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Uncle);
|
||||||
net.peer_mut(1).chain.add_blocks(1, false);
|
net.peer_mut(1).chain.add_blocks(1, EachBlockWith::Uncle);
|
||||||
|
|
||||||
net.start();
|
net.start();
|
||||||
|
|
||||||
@ -123,13 +124,13 @@ fn status_packet() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn propagate_hashes() {
|
fn propagate_hashes() {
|
||||||
let mut net = TestNet::new(6);
|
let mut net = TestNet::new(6);
|
||||||
net.peer_mut(1).chain.add_blocks(10, false);
|
net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||||
net.sync();
|
net.sync();
|
||||||
|
|
||||||
net.peer_mut(0).chain.add_blocks(10, false);
|
net.peer_mut(0).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||||
net.sync();
|
net.sync();
|
||||||
net.trigger_block_verified(0); //first event just sets the marker
|
net.trigger_chain_new_blocks(0); //first event just sets the marker
|
||||||
net.trigger_block_verified(0);
|
net.trigger_chain_new_blocks(0);
|
||||||
|
|
||||||
// 5 peers to sync
|
// 5 peers to sync
|
||||||
assert_eq!(5, net.peer(0).queue.len());
|
assert_eq!(5, net.peer(0).queue.len());
|
||||||
@ -149,12 +150,12 @@ fn propagate_hashes() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn propagate_blocks() {
|
fn propagate_blocks() {
|
||||||
let mut net = TestNet::new(2);
|
let mut net = TestNet::new(2);
|
||||||
net.peer_mut(1).chain.add_blocks(10, false);
|
net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||||
net.sync();
|
net.sync();
|
||||||
|
|
||||||
net.peer_mut(0).chain.add_blocks(10, false);
|
net.peer_mut(0).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||||
net.trigger_block_verified(0); //first event just sets the marker
|
net.trigger_chain_new_blocks(0); //first event just sets the marker
|
||||||
net.trigger_block_verified(0);
|
net.trigger_chain_new_blocks(0);
|
||||||
|
|
||||||
assert!(!net.peer(0).queue.is_empty());
|
assert!(!net.peer(0).queue.is_empty());
|
||||||
// NEW_BLOCK_PACKET
|
// NEW_BLOCK_PACKET
|
||||||
@ -164,7 +165,7 @@ fn propagate_blocks() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn restart_on_malformed_block() {
|
fn restart_on_malformed_block() {
|
||||||
let mut net = TestNet::new(2);
|
let mut net = TestNet::new(2);
|
||||||
net.peer_mut(1).chain.add_blocks(10, false);
|
net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||||
net.peer_mut(1).chain.corrupt_block(6);
|
net.peer_mut(1).chain.corrupt_block(6);
|
||||||
net.sync_steps(10);
|
net.sync_steps(10);
|
||||||
|
|
||||||
|
@ -22,9 +22,10 @@ use io::SyncIo;
|
|||||||
use chain::ChainSync;
|
use chain::ChainSync;
|
||||||
use ::SyncConfig;
|
use ::SyncConfig;
|
||||||
use ethcore::receipt::Receipt;
|
use ethcore::receipt::Receipt;
|
||||||
use ethcore::transaction::LocalizedTransaction;
|
use ethcore::transaction::{LocalizedTransaction, Transaction, Action};
|
||||||
use ethcore::filter::Filter;
|
use ethcore::filter::Filter;
|
||||||
use ethcore::log_entry::LocalizedLogEntry;
|
use ethcore::log_entry::LocalizedLogEntry;
|
||||||
|
use ethcore::block::ClosedBlock;
|
||||||
|
|
||||||
pub struct TestBlockChainClient {
|
pub struct TestBlockChainClient {
|
||||||
pub blocks: RwLock<HashMap<H256, Bytes>>,
|
pub blocks: RwLock<HashMap<H256, Bytes>>,
|
||||||
@ -34,6 +35,14 @@ pub struct TestBlockChainClient {
|
|||||||
pub difficulty: RwLock<U256>,
|
pub difficulty: RwLock<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum EachBlockWith {
|
||||||
|
Nothing,
|
||||||
|
Uncle,
|
||||||
|
Transaction,
|
||||||
|
UncleAndTransaction
|
||||||
|
}
|
||||||
|
|
||||||
impl TestBlockChainClient {
|
impl TestBlockChainClient {
|
||||||
pub fn new() -> TestBlockChainClient {
|
pub fn new() -> TestBlockChainClient {
|
||||||
|
|
||||||
@ -44,30 +53,53 @@ impl TestBlockChainClient {
|
|||||||
last_hash: RwLock::new(H256::new()),
|
last_hash: RwLock::new(H256::new()),
|
||||||
difficulty: RwLock::new(From::from(0)),
|
difficulty: RwLock::new(From::from(0)),
|
||||||
};
|
};
|
||||||
client.add_blocks(1, true); // add genesis block
|
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||||
client.genesis_hash = client.last_hash.read().unwrap().clone();
|
client.genesis_hash = client.last_hash.read().unwrap().clone();
|
||||||
client
|
client
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_blocks(&mut self, count: usize, empty: bool) {
|
pub fn add_blocks(&mut self, count: usize, with: EachBlockWith) {
|
||||||
let len = self.numbers.read().unwrap().len();
|
let len = self.numbers.read().unwrap().len();
|
||||||
for n in len..(len + count) {
|
for n in len..(len + count) {
|
||||||
let mut header = BlockHeader::new();
|
let mut header = BlockHeader::new();
|
||||||
header.difficulty = From::from(n);
|
header.difficulty = From::from(n);
|
||||||
header.parent_hash = self.last_hash.read().unwrap().clone();
|
header.parent_hash = self.last_hash.read().unwrap().clone();
|
||||||
header.number = n as BlockNumber;
|
header.number = n as BlockNumber;
|
||||||
let mut uncles = RlpStream::new_list(if empty {0} else {1});
|
let uncles = match with {
|
||||||
if !empty {
|
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
|
||||||
|
let mut uncles = RlpStream::new_list(1);
|
||||||
let mut uncle_header = BlockHeader::new();
|
let mut uncle_header = BlockHeader::new();
|
||||||
uncle_header.difficulty = From::from(n);
|
uncle_header.difficulty = From::from(n);
|
||||||
uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
|
uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
|
||||||
uncle_header.number = n as BlockNumber;
|
uncle_header.number = n as BlockNumber;
|
||||||
uncles.append(&uncle_header);
|
uncles.append(&uncle_header);
|
||||||
header.uncles_hash = uncles.as_raw().sha3();
|
header.uncles_hash = uncles.as_raw().sha3();
|
||||||
}
|
uncles
|
||||||
|
},
|
||||||
|
_ => RlpStream::new_list(0)
|
||||||
|
};
|
||||||
|
let txs = match with {
|
||||||
|
EachBlockWith::Transaction | EachBlockWith::UncleAndTransaction => {
|
||||||
|
let mut txs = RlpStream::new_list(1);
|
||||||
|
let keypair = KeyPair::create().unwrap();
|
||||||
|
let tx = Transaction {
|
||||||
|
action: Action::Create,
|
||||||
|
value: U256::from(100),
|
||||||
|
data: "3331600055".from_hex().unwrap(),
|
||||||
|
gas: U256::from(100_000),
|
||||||
|
gas_price: U256::one(),
|
||||||
|
nonce: U256::zero()
|
||||||
|
};
|
||||||
|
let signed_tx = tx.sign(&keypair.secret());
|
||||||
|
txs.append(&signed_tx);
|
||||||
|
txs.out()
|
||||||
|
},
|
||||||
|
_ => rlp::NULL_RLP.to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
let mut rlp = RlpStream::new_list(3);
|
let mut rlp = RlpStream::new_list(3);
|
||||||
rlp.append(&header);
|
rlp.append(&header);
|
||||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
rlp.append_raw(&txs, 1);
|
||||||
rlp.append_raw(uncles.as_raw(), 1);
|
rlp.append_raw(uncles.as_raw(), 1);
|
||||||
self.import_block(rlp.as_raw().to_vec()).unwrap();
|
self.import_block(rlp.as_raw().to_vec()).unwrap();
|
||||||
}
|
}
|
||||||
@ -109,6 +141,10 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn nonce(&self, _address: &Address) -> U256 {
|
||||||
|
U256::zero()
|
||||||
|
}
|
||||||
|
|
||||||
fn code(&self, _address: &Address) -> Option<Bytes> {
|
fn code(&self, _address: &Address) -> Option<Bytes> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
@ -125,6 +161,14 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_seal(&self, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||||
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
|
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
|
||||||
}
|
}
|
||||||
@ -420,8 +464,8 @@ impl TestNet {
|
|||||||
self.peers.iter().all(|p| p.queue.is_empty())
|
self.peers.iter().all(|p| p.queue.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trigger_block_verified(&mut self, peer_id: usize) {
|
pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) {
|
||||||
let mut peer = self.peer_mut(peer_id);
|
let mut peer = self.peer_mut(peer_id);
|
||||||
peer.sync.chain_blocks_verified(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None));
|
peer.sync.chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,67 @@
|
|||||||
// TODO [todr] - own transactions should have higher priority
|
// TODO [todr] - own transactions should have higher priority
|
||||||
|
|
||||||
//! Transaction Queue
|
//! Transaction Queue
|
||||||
|
//!
|
||||||
|
//! TransactionQueue keeps track of all transactions seen by the node (received from other peers) and own transactions
|
||||||
|
//! and orders them by priority. Top priority transactions are those with low nonce height (difference between
|
||||||
|
//! transaction's nonce and next nonce expected from this sender). If nonces are equal transaction's gas price is used
|
||||||
|
//! for comparison (higher gas price = higher priority).
|
||||||
|
//!
|
||||||
|
//! # Usage Example
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! extern crate ethcore_util as util;
|
||||||
|
//! extern crate ethcore;
|
||||||
|
//! extern crate ethsync;
|
||||||
|
//! extern crate rustc_serialize;
|
||||||
|
//!
|
||||||
|
//! use util::crypto::KeyPair;
|
||||||
|
//! use util::hash::Address;
|
||||||
|
//! use util::numbers::{Uint, U256};
|
||||||
|
//! use ethsync::TransactionQueue;
|
||||||
|
//! use ethcore::transaction::*;
|
||||||
|
//! use rustc_serialize::hex::FromHex;
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let key = KeyPair::create().unwrap();
|
||||||
|
//! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||||
|
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(10) };
|
||||||
|
//! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||||
|
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(11) };
|
||||||
|
//!
|
||||||
|
//! let st1 = t1.sign(&key.secret());
|
||||||
|
//! let st2 = t2.sign(&key.secret());
|
||||||
|
//! let default_nonce = |_a: &Address| U256::from(10);
|
||||||
|
//!
|
||||||
|
//! let mut txq = TransactionQueue::new();
|
||||||
|
//! txq.add(st2.clone(), &default_nonce);
|
||||||
|
//! txq.add(st1.clone(), &default_nonce);
|
||||||
|
//!
|
||||||
|
//! // Check status
|
||||||
|
//! assert_eq!(txq.status().pending, 2);
|
||||||
|
//! // Check top transactions
|
||||||
|
//! let top = txq.top_transactions(3);
|
||||||
|
//! assert_eq!(top.len(), 2);
|
||||||
|
//! assert_eq!(top[0], st1);
|
||||||
|
//! assert_eq!(top[1], st2);
|
||||||
|
//!
|
||||||
|
//! // And when transaction is removed (but nonce haven't changed)
|
||||||
|
//! // it will move invalid transactions to future
|
||||||
|
//! txq.remove(&st1.hash(), &default_nonce);
|
||||||
|
//! assert_eq!(txq.status().pending, 0);
|
||||||
|
//! assert_eq!(txq.status().future, 1);
|
||||||
|
//! assert_eq!(txq.top_transactions(3).len(), 0);
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Maintaing valid state
|
||||||
|
//!
|
||||||
|
//! 1. Whenever transaction is imported to queue (to queue) all other transactions from this sender are revalidated in current. It means that they are moved to future and back again (height recalculation & gap filling).
|
||||||
|
//! 2. Whenever transaction is removed:
|
||||||
|
//! - When it's removed from `future` - all `future` transactions heights are recalculated and then
|
||||||
|
//! we check if the transactions should go to `current` (comparing state nonce)
|
||||||
|
//! - When it's removed from `current` - all transactions from this sender (`current` & `future`) are recalculated.
|
||||||
|
//!
|
||||||
|
|
||||||
use std::cmp::{Ordering};
|
use std::cmp::{Ordering};
|
||||||
use std::collections::{HashMap, BTreeSet};
|
use std::collections::{HashMap, BTreeSet};
|
||||||
@ -24,12 +85,20 @@ use util::numbers::{Uint, U256};
|
|||||||
use util::hash::{Address, H256};
|
use util::hash::{Address, H256};
|
||||||
use util::table::*;
|
use util::table::*;
|
||||||
use ethcore::transaction::*;
|
use ethcore::transaction::*;
|
||||||
|
use ethcore::error::Error;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
/// Light structure used to identify transaction and it's order
|
||||||
struct TransactionOrder {
|
struct TransactionOrder {
|
||||||
|
/// Primary ordering factory. Difference between transaction nonce and expected nonce in state
|
||||||
|
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
|
||||||
|
/// High nonce_height = Low priority (processed later)
|
||||||
nonce_height: U256,
|
nonce_height: U256,
|
||||||
|
/// Gas Price of the transaction.
|
||||||
|
/// Low gas price = Low priority (processed later)
|
||||||
gas_price: U256,
|
gas_price: U256,
|
||||||
|
/// Hash to identify associated transaction
|
||||||
hash: H256,
|
hash: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +139,7 @@ impl Ord for TransactionOrder {
|
|||||||
let a_gas = self.gas_price;
|
let a_gas = self.gas_price;
|
||||||
let b_gas = b.gas_price;
|
let b_gas = b.gas_price;
|
||||||
if a_gas != b_gas {
|
if a_gas != b_gas {
|
||||||
return a_gas.cmp(&b_gas);
|
return b_gas.cmp(&a_gas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare hashes
|
// Compare hashes
|
||||||
@ -78,14 +147,16 @@ impl Ord for TransactionOrder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verified transaction (with sender)
|
||||||
struct VerifiedTransaction {
|
struct VerifiedTransaction {
|
||||||
transaction: SignedTransaction
|
transaction: SignedTransaction
|
||||||
}
|
}
|
||||||
impl VerifiedTransaction {
|
impl VerifiedTransaction {
|
||||||
fn new(transaction: SignedTransaction) -> Self {
|
fn new(transaction: SignedTransaction) -> Result<Self, Error> {
|
||||||
VerifiedTransaction {
|
try!(transaction.sender());
|
||||||
|
Ok(VerifiedTransaction {
|
||||||
transaction: transaction
|
transaction: transaction
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash(&self) -> H256 {
|
fn hash(&self) -> H256 {
|
||||||
@ -101,6 +172,11 @@ impl VerifiedTransaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Holds transactions accessible by (address, nonce) and by priority
|
||||||
|
///
|
||||||
|
/// TransactionSet keeps number of entries below limit, but it doesn't
|
||||||
|
/// automatically happen during `insert/remove` operations.
|
||||||
|
/// You have to call `enforce_limit` to remove lowest priority transactions from set.
|
||||||
struct TransactionSet {
|
struct TransactionSet {
|
||||||
by_priority: BTreeSet<TransactionOrder>,
|
by_priority: BTreeSet<TransactionOrder>,
|
||||||
by_address: Table<Address, U256, TransactionOrder>,
|
by_address: Table<Address, U256, TransactionOrder>,
|
||||||
@ -108,30 +184,37 @@ struct TransactionSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionSet {
|
impl TransactionSet {
|
||||||
fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) {
|
/// Inserts `TransactionOrder` to this set
|
||||||
|
fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option<TransactionOrder> {
|
||||||
self.by_priority.insert(order.clone());
|
self.by_priority.insert(order.clone());
|
||||||
self.by_address.insert(sender, nonce, order);
|
self.by_address.insert(sender, nonce, order)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce_limit(&mut self, by_hash: &HashMap<H256, VerifiedTransaction>) {
|
/// Remove low priority transactions if there is more then specified by given `limit`.
|
||||||
|
///
|
||||||
|
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
||||||
|
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) {
|
||||||
let len = self.by_priority.len();
|
let len = self.by_priority.len();
|
||||||
if len <= self.limit {
|
if len <= self.limit {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let to_drop : Vec<&VerifiedTransaction> = {
|
let to_drop : Vec<(Address, U256)> = {
|
||||||
self.by_priority
|
self.by_priority
|
||||||
.iter()
|
.iter()
|
||||||
.skip(self.limit)
|
.skip(self.limit)
|
||||||
.map(|order| by_hash.get(&order.hash).expect("Inconsistency in queue detected."))
|
.map(|order| by_hash.get(&order.hash).expect("Inconsistency in queue detected."))
|
||||||
|
.map(|tx| (tx.sender(), tx.nonce()))
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
for tx in to_drop {
|
for (sender, nonce) in to_drop {
|
||||||
self.drop(&tx.sender(), &tx.nonce());
|
let order = self.drop(&sender, &nonce).expect("Dropping transaction found in priority queue failed.");
|
||||||
|
by_hash.remove(&order.hash).expect("Inconsistency in queue.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drop transaction from this set (remove from `by_priority` and `by_address`)
|
||||||
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
|
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
|
||||||
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
|
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
|
||||||
self.by_priority.remove(&tx_order);
|
self.by_priority.remove(&tx_order);
|
||||||
@ -140,12 +223,15 @@ impl TransactionSet {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drop all transactions.
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
self.by_priority.clear();
|
self.by_priority.clear();
|
||||||
self.by_address.clear();
|
self.by_address.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Will be used when rpc merged
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Current status of the queue
|
/// Current status of the queue
|
||||||
pub struct TransactionQueueStatus {
|
pub struct TransactionQueueStatus {
|
||||||
@ -194,6 +280,8 @@ impl TransactionQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Will be used when rpc merged
|
||||||
|
#[allow(dead_code)]
|
||||||
/// Returns current status for this queue
|
/// Returns current status for this queue
|
||||||
pub fn status(&self) -> TransactionQueueStatus {
|
pub fn status(&self) -> TransactionQueueStatus {
|
||||||
TransactionQueueStatus {
|
TransactionQueueStatus {
|
||||||
@ -203,60 +291,95 @@ impl TransactionQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds all signed transactions to queue to be verified and imported
|
/// Adds all signed transactions to queue to be verified and imported
|
||||||
pub fn add_all<T>(&mut self, txs: Vec<SignedTransaction>, fetch_nonce: T)
|
pub fn add_all<T>(&mut self, txs: Vec<SignedTransaction>, fetch_nonce: T) -> Result<(), Error>
|
||||||
where T: Fn(&Address) -> U256 {
|
where T: Fn(&Address) -> U256 {
|
||||||
for tx in txs.into_iter() {
|
for tx in txs.into_iter() {
|
||||||
self.add(tx, &fetch_nonce);
|
try!(self.add(tx, &fetch_nonce));
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add signed transaction to queue to be verified and imported
|
/// Add signed transaction to queue to be verified and imported
|
||||||
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_nonce: &T)
|
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_nonce: &T) -> Result<(), Error>
|
||||||
where T: Fn(&Address) -> U256 {
|
where T: Fn(&Address) -> U256 {
|
||||||
self.import_tx(VerifiedTransaction::new(tx), fetch_nonce);
|
self.import_tx(try!(VerifiedTransaction::new(tx)), fetch_nonce);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all transactions identified by hashes given in slice
|
/// Removes all transactions identified by hashes given in slice
|
||||||
///
|
///
|
||||||
/// If gap is introduced marks subsequent transactions as future
|
/// If gap is introduced marks subsequent transactions as future
|
||||||
pub fn remove_all<T>(&mut self, txs: &[H256], fetch_nonce: T)
|
pub fn remove_all<T>(&mut self, transaction_hashes: &[H256], fetch_nonce: T)
|
||||||
where T: Fn(&Address) -> U256 {
|
where T: Fn(&Address) -> U256 {
|
||||||
for tx in txs {
|
for hash in transaction_hashes {
|
||||||
self.remove(&tx, &fetch_nonce);
|
self.remove(&hash, &fetch_nonce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes transaction identified by hashes from queue.
|
/// Removes transaction identified by hashes from queue.
|
||||||
///
|
///
|
||||||
/// If gap is introduced marks subsequent transactions as future
|
/// If gap is introduced marks subsequent transactions as future
|
||||||
pub fn remove<T>(&mut self, hash: &H256, fetch_nonce: &T)
|
pub fn remove<T>(&mut self, transaction_hash: &H256, fetch_nonce: &T)
|
||||||
where T: Fn(&Address) -> U256 {
|
where T: Fn(&Address) -> U256 {
|
||||||
let transaction = self.by_hash.remove(hash);
|
let transaction = self.by_hash.remove(transaction_hash);
|
||||||
if transaction.is_none() {
|
if transaction.is_none() {
|
||||||
// We don't know this transaction
|
// We don't know this transaction
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let transaction = transaction.unwrap();
|
let transaction = transaction.unwrap();
|
||||||
let sender = transaction.sender();
|
let sender = transaction.sender();
|
||||||
let nonce = transaction.nonce();
|
let nonce = transaction.nonce();
|
||||||
|
let current_nonce = fetch_nonce(&sender);
|
||||||
|
|
||||||
println!("Removing tx: {:?}", transaction.transaction);
|
|
||||||
// Remove from future
|
// Remove from future
|
||||||
self.future.drop(&sender, &nonce);
|
let order = self.future.drop(&sender, &nonce);
|
||||||
|
if order.is_some() {
|
||||||
// Remove from current
|
self.update_future(&sender, current_nonce);
|
||||||
let order = self.current.drop(&sender, &nonce);
|
// And now lets check if there is some chain of transactions in future
|
||||||
if order.is_none() {
|
// that should be placed in current
|
||||||
|
self.move_matching_future_to_current(sender.clone(), current_nonce, current_nonce);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's remove transactions where tx.nonce < current_nonce
|
// Remove from current
|
||||||
// and if there are any future transactions matching current_nonce+1 - move to current
|
let order = self.current.drop(&sender, &nonce);
|
||||||
let current_nonce = fetch_nonce(&sender);
|
if order.is_some() {
|
||||||
// We will either move transaction to future or remove it completely
|
// We will either move transaction to future or remove it completely
|
||||||
// so there will be no transactions from this sender in current
|
// so there will be no transactions from this sender in current
|
||||||
self.last_nonces.remove(&sender);
|
self.last_nonces.remove(&sender);
|
||||||
|
// First update height of transactions in future to avoid collisions
|
||||||
|
self.update_future(&sender, current_nonce);
|
||||||
|
// This should move all current transactions to future and remove old transactions
|
||||||
|
self.move_all_to_future(&sender, current_nonce);
|
||||||
|
// And now lets check if there is some chain of transactions in future
|
||||||
|
// that should be placed in current. It should also update last_nonces.
|
||||||
|
self.move_matching_future_to_current(sender.clone(), current_nonce, current_nonce);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update height of all transactions in future transactions set.
|
||||||
|
fn update_future(&mut self, sender: &Address, current_nonce: U256) {
|
||||||
|
// We need to drain all transactions for current sender from future and reinsert them with updated height
|
||||||
|
let all_nonces_from_sender = match self.future.by_address.row(&sender) {
|
||||||
|
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
for k in all_nonces_from_sender {
|
||||||
|
let order = self.future.drop(&sender, &k).unwrap();
|
||||||
|
if k >= current_nonce {
|
||||||
|
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
|
||||||
|
} else {
|
||||||
|
// Remove the transaction completely
|
||||||
|
self.by_hash.remove(&order.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Drop all transactions from given sender from `current`.
|
||||||
|
/// Either moves them to `future` or removes them from queue completely.
|
||||||
|
fn move_all_to_future(&mut self, sender: &Address, current_nonce: U256) {
|
||||||
let all_nonces_from_sender = match self.current.by_address.row(&sender) {
|
let all_nonces_from_sender = match self.current.by_address.row(&sender) {
|
||||||
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
|
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
|
||||||
None => vec![],
|
None => vec![],
|
||||||
@ -266,22 +389,17 @@ impl TransactionQueue {
|
|||||||
// Goes to future or is removed
|
// Goes to future or is removed
|
||||||
let order = self.current.drop(&sender, &k).unwrap();
|
let order = self.current.drop(&sender, &k).unwrap();
|
||||||
if k >= current_nonce {
|
if k >= current_nonce {
|
||||||
println!("Moving to future: {:?}", order);
|
|
||||||
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
|
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
|
||||||
} else {
|
} else {
|
||||||
self.by_hash.remove(&order.hash);
|
self.by_hash.remove(&order.hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.future.enforce_limit(&self.by_hash);
|
self.future.enforce_limit(&mut self.by_hash);
|
||||||
|
|
||||||
// And now lets check if there is some chain of transactions in future
|
|
||||||
// that should be placed in current
|
|
||||||
if let Some(new_current_top) = self.move_future_txs(sender.clone(), current_nonce - U256::one(), current_nonce) {
|
|
||||||
self.last_nonces.insert(sender, new_current_top);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns top transactions from the queue
|
// Will be used when mining merged
|
||||||
|
#[allow(dead_code)]
|
||||||
|
/// Returns top transactions from the queue ordered by priority.
|
||||||
pub fn top_transactions(&self, size: usize) -> Vec<SignedTransaction> {
|
pub fn top_transactions(&self, size: usize) -> Vec<SignedTransaction> {
|
||||||
self.current.by_priority
|
self.current.by_priority
|
||||||
.iter()
|
.iter()
|
||||||
@ -299,67 +417,102 @@ impl TransactionQueue {
|
|||||||
self.last_nonces.clear();
|
self.last_nonces.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_future_txs(&mut self, address: Address, current_nonce: U256, first_nonce: U256) -> Option<U256> {
|
/// Checks if there are any transactions in `future` that should actually be promoted to `current`
|
||||||
println!("Moving from future for: {:?} base: {:?}", current_nonce, first_nonce);
|
/// (because nonce matches).
|
||||||
let mut current_nonce = current_nonce + U256::one();
|
fn move_matching_future_to_current(&mut self, address: Address, mut current_nonce: U256, first_nonce: U256) {
|
||||||
{
|
{
|
||||||
let by_nonce = self.future.by_address.row_mut(&address);
|
let by_nonce = self.future.by_address.row_mut(&address);
|
||||||
if let None = by_nonce {
|
if let None = by_nonce {
|
||||||
return None;
|
return;
|
||||||
}
|
}
|
||||||
let mut by_nonce = by_nonce.unwrap();
|
let mut by_nonce = by_nonce.unwrap();
|
||||||
while let Some(order) = by_nonce.remove(¤t_nonce) {
|
while let Some(order) = by_nonce.remove(¤t_nonce) {
|
||||||
// remove also from priority and hash
|
// remove also from priority and hash
|
||||||
self.future.by_priority.remove(&order);
|
self.future.by_priority.remove(&order);
|
||||||
// Put to current
|
// Put to current
|
||||||
println!("Moved: {:?}", order);
|
|
||||||
let order = order.update_height(current_nonce.clone(), first_nonce);
|
let order = order.update_height(current_nonce.clone(), first_nonce);
|
||||||
self.current.insert(address.clone(), current_nonce, order);
|
self.current.insert(address.clone(), current_nonce, order);
|
||||||
current_nonce = current_nonce + U256::one();
|
current_nonce = current_nonce + U256::one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.future.by_address.clear_if_empty(&address);
|
self.future.by_address.clear_if_empty(&address);
|
||||||
// Returns last inserted nonce
|
// Update last inserted nonce
|
||||||
Some(current_nonce - U256::one())
|
self.last_nonces.insert(address, current_nonce - U256::one());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds VerifiedTransaction to this queue.
|
||||||
|
///
|
||||||
|
/// Determines if it should be placed in current or future. When transaction is
|
||||||
|
/// imported to `current` also checks if there are any `future` transactions that should be promoted because of
|
||||||
|
/// this.
|
||||||
|
///
|
||||||
|
/// It ignores transactions that has already been imported (same `hash`) and replaces the transaction
|
||||||
|
/// iff `(address, nonce)` is the same but `gas_price` is higher.
|
||||||
fn import_tx<T>(&mut self, tx: VerifiedTransaction, fetch_nonce: &T)
|
fn import_tx<T>(&mut self, tx: VerifiedTransaction, fetch_nonce: &T)
|
||||||
where T: Fn(&Address) -> U256 {
|
where T: Fn(&Address) -> U256 {
|
||||||
let nonce = tx.nonce();
|
|
||||||
let address = tx.sender();
|
|
||||||
|
|
||||||
|
if self.by_hash.get(&tx.hash()).is_some() {
|
||||||
|
// Transaction is already imported.
|
||||||
|
trace!(target: "sync", "Dropping already imported transaction with hash: {:?}", tx.hash());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let address = tx.sender();
|
||||||
|
let nonce = tx.nonce();
|
||||||
|
|
||||||
|
let state_nonce = fetch_nonce(&address);
|
||||||
let next_nonce = self.last_nonces
|
let next_nonce = self.last_nonces
|
||||||
.get(&address)
|
.get(&address)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map_or_else(|| fetch_nonce(&address), |n| n + U256::one());
|
.map_or(state_nonce, |n| n + U256::one());
|
||||||
|
|
||||||
println!("Expected next: {:?}, got: {:?}", next_nonce, nonce);
|
|
||||||
// Check height
|
// Check height
|
||||||
if nonce > next_nonce {
|
if nonce > next_nonce {
|
||||||
let order = TransactionOrder::for_transaction(&tx, next_nonce);
|
|
||||||
// Insert to by_hash
|
|
||||||
self.by_hash.insert(tx.hash(), tx);
|
|
||||||
// We have a gap - put to future
|
// We have a gap - put to future
|
||||||
self.future.insert(address, nonce, order);
|
Self::replace_transaction(tx, next_nonce, &mut self.future, &mut self.by_hash);
|
||||||
self.future.enforce_limit(&self.by_hash);
|
self.future.enforce_limit(&mut self.by_hash);
|
||||||
return;
|
return;
|
||||||
} else if next_nonce > nonce {
|
} else if nonce < state_nonce {
|
||||||
// Droping transaction
|
// Droping transaction
|
||||||
|
trace!(target: "sync", "Dropping transaction with nonce: {} - expecting: {}", nonce, next_nonce);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let base_nonce = fetch_nonce(&address);
|
let base_nonce = fetch_nonce(&address);
|
||||||
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
Self::replace_transaction(tx, base_nonce.clone(), &mut self.current, &mut self.by_hash);
|
||||||
// Insert to by_hash
|
self.last_nonces.insert(address.clone(), nonce);
|
||||||
self.by_hash.insert(tx.hash(), tx);
|
|
||||||
|
|
||||||
// Insert to current
|
|
||||||
self.current.insert(address.clone(), nonce, order);
|
|
||||||
// But maybe there are some more items waiting in future?
|
// But maybe there are some more items waiting in future?
|
||||||
let new_last_nonce = self.move_future_txs(address.clone(), nonce, base_nonce);
|
self.move_matching_future_to_current(address.clone(), nonce + U256::one(), base_nonce);
|
||||||
self.last_nonces.insert(address.clone(), new_last_nonce.unwrap_or(nonce));
|
self.current.enforce_limit(&mut self.by_hash);
|
||||||
// Enforce limit
|
}
|
||||||
self.current.enforce_limit(&self.by_hash);
|
|
||||||
|
/// Replaces transaction in given set (could be `future` or `current`).
|
||||||
|
///
|
||||||
|
/// If there is already transaction with same `(sender, nonce)` it will be replaced iff `gas_price` is higher.
|
||||||
|
/// One of the transactions is dropped from set and also removed from queue entirely (from `by_hash`).
|
||||||
|
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) {
|
||||||
|
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
||||||
|
let hash = tx.hash();
|
||||||
|
let address = tx.sender();
|
||||||
|
let nonce = tx.nonce();
|
||||||
|
|
||||||
|
by_hash.insert(hash.clone(), tx);
|
||||||
|
if let Some(old) = set.insert(address, nonce, order.clone()) {
|
||||||
|
// There was already transaction in queue. Let's check which one should stay
|
||||||
|
let old_fee = old.gas_price;
|
||||||
|
let new_fee = order.gas_price;
|
||||||
|
if old_fee.cmp(&new_fee) == Ordering::Greater {
|
||||||
|
// Put back old transaction since it has greater priority (higher gas_price)
|
||||||
|
set.by_address.insert(address, nonce, old);
|
||||||
|
// and remove new one
|
||||||
|
set.by_priority.remove(&order);
|
||||||
|
by_hash.remove(&hash);
|
||||||
|
} else {
|
||||||
|
// Make sure we remove old transaction entirely
|
||||||
|
set.by_priority.remove(&old);
|
||||||
|
by_hash.remove(&old.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,12 +520,8 @@ impl TransactionQueue {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
use self::rustc_serialize::hex::FromHex;
|
|
||||||
use std::collections::{HashMap, BTreeSet};
|
|
||||||
use util::crypto::KeyPair;
|
|
||||||
use util::numbers::{U256, Uint};
|
|
||||||
use util::hash::{Address};
|
|
||||||
use util::table::*;
|
use util::table::*;
|
||||||
|
use util::*;
|
||||||
use ethcore::transaction::*;
|
use ethcore::transaction::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
||||||
@ -416,12 +565,12 @@ mod test {
|
|||||||
limit: 1
|
limit: 1
|
||||||
};
|
};
|
||||||
let (tx1, tx2) = new_txs(U256::from(1));
|
let (tx1, tx2) = new_txs(U256::from(1));
|
||||||
let tx1 = VerifiedTransaction::new(tx1);
|
let tx1 = VerifiedTransaction::new(tx1).unwrap();
|
||||||
let tx2 = VerifiedTransaction::new(tx2);
|
let tx2 = VerifiedTransaction::new(tx2).unwrap();
|
||||||
let by_hash = {
|
let mut by_hash = {
|
||||||
let mut x = HashMap::new();
|
let mut x = HashMap::new();
|
||||||
let tx1 = VerifiedTransaction::new(tx1.transaction.clone());
|
let tx1 = VerifiedTransaction::new(tx1.transaction.clone()).unwrap();
|
||||||
let tx2 = VerifiedTransaction::new(tx2.transaction.clone());
|
let tx2 = VerifiedTransaction::new(tx2.transaction.clone()).unwrap();
|
||||||
x.insert(tx1.hash(), tx1);
|
x.insert(tx1.hash(), tx1);
|
||||||
x.insert(tx2.hash(), tx2);
|
x.insert(tx2.hash(), tx2);
|
||||||
x
|
x
|
||||||
@ -435,9 +584,10 @@ mod test {
|
|||||||
assert_eq!(set.by_address.len(), 2);
|
assert_eq!(set.by_address.len(), 2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
set.enforce_limit(&by_hash);
|
set.enforce_limit(&mut by_hash);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(by_hash.len(), 1);
|
||||||
assert_eq!(set.by_priority.len(), 1);
|
assert_eq!(set.by_priority.len(), 1);
|
||||||
assert_eq!(set.by_address.len(), 1);
|
assert_eq!(set.by_address.len(), 1);
|
||||||
assert_eq!(set.by_priority.iter().next().unwrap().clone(), order1);
|
assert_eq!(set.by_priority.iter().next().unwrap().clone(), order1);
|
||||||
@ -454,13 +604,39 @@ mod test {
|
|||||||
let tx = new_tx();
|
let tx = new_tx();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx, &default_nonce);
|
let res = txq.add(tx, &default_nonce);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert!(res.is_ok());
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 1);
|
assert_eq!(stats.pending, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_reject_incorectly_signed_transaction() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let tx = new_unsigned_tx(U256::from(123));
|
||||||
|
let stx = {
|
||||||
|
let mut s = RlpStream::new_list(9);
|
||||||
|
s.append(&tx.nonce);
|
||||||
|
s.append(&tx.gas_price);
|
||||||
|
s.append(&tx.gas);
|
||||||
|
s.append_empty_data(); // action=create
|
||||||
|
s.append(&tx.value);
|
||||||
|
s.append(&tx.data);
|
||||||
|
s.append(&0u64); // v
|
||||||
|
s.append(&U256::zero()); // r
|
||||||
|
s.append(&U256::zero()); // s
|
||||||
|
decode(s.as_raw())
|
||||||
|
};
|
||||||
|
// when
|
||||||
|
let res = txq.add(stx, &default_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_import_txs_from_same_sender() {
|
fn should_import_txs_from_same_sender() {
|
||||||
// given
|
// given
|
||||||
@ -469,8 +645,8 @@ mod test {
|
|||||||
let (tx, tx2) = new_txs(U256::from(1));
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx.clone(), &default_nonce);
|
txq.add(tx.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx2.clone(), &default_nonce);
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let top = txq.top_transactions(5);
|
let top = txq.top_transactions(5);
|
||||||
@ -487,8 +663,8 @@ mod test {
|
|||||||
let (tx, tx2) = new_txs(U256::from(2));
|
let (tx, tx2) = new_txs(U256::from(2));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx.clone(), &default_nonce);
|
txq.add(tx.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx2.clone(), &default_nonce);
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
@ -499,6 +675,28 @@ mod test {
|
|||||||
assert_eq!(top[0], tx);
|
assert_eq!(top[0], tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_correctly_update_futures_when_removing() {
|
||||||
|
// given
|
||||||
|
let prev_nonce = |a: &Address| default_nonce(a) - U256::one();
|
||||||
|
let next2_nonce = |a: &Address| default_nonce(a) + U256::from(2);
|
||||||
|
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
|
||||||
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
|
txq.add(tx.clone(), &prev_nonce);
|
||||||
|
txq.add(tx2.clone(), &prev_nonce);
|
||||||
|
assert_eq!(txq.status().future, 2);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.remove(&tx.hash(), &next2_nonce);
|
||||||
|
// should remove both transactions since they are not valid
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(txq.status().pending, 0);
|
||||||
|
assert_eq!(txq.status().future, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_move_transactions_if_gap_filled() {
|
fn should_move_transactions_if_gap_filled() {
|
||||||
// given
|
// given
|
||||||
@ -509,13 +707,13 @@ mod test {
|
|||||||
let tx1 = new_unsigned_tx(U256::from(124)).sign(&secret);
|
let tx1 = new_unsigned_tx(U256::from(124)).sign(&secret);
|
||||||
let tx2 = new_unsigned_tx(U256::from(125)).sign(&secret);
|
let tx2 = new_unsigned_tx(U256::from(125)).sign(&secret);
|
||||||
|
|
||||||
txq.add(tx, &default_nonce);
|
txq.add(tx, &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().pending, 1);
|
assert_eq!(txq.status().pending, 1);
|
||||||
txq.add(tx2, &default_nonce);
|
txq.add(tx2, &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx1, &default_nonce);
|
txq.add(tx1, &default_nonce).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
@ -528,8 +726,8 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq2 = TransactionQueue::new();
|
let mut txq2 = TransactionQueue::new();
|
||||||
let (tx, tx2) = new_txs(U256::from(3));
|
let (tx, tx2) = new_txs(U256::from(3));
|
||||||
txq2.add(tx.clone(), &default_nonce);
|
txq2.add(tx.clone(), &default_nonce).unwrap();
|
||||||
txq2.add(tx2.clone(), &default_nonce);
|
txq2.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq2.status().pending, 1);
|
assert_eq!(txq2.status().pending, 1);
|
||||||
assert_eq!(txq2.status().future, 1);
|
assert_eq!(txq2.status().future, 1);
|
||||||
|
|
||||||
@ -550,10 +748,10 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::new();
|
||||||
let (tx, tx2) = new_txs(U256::from(1));
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
let tx3 = new_tx();
|
let tx3 = new_tx();
|
||||||
txq.add(tx2.clone(), &default_nonce);
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx3.clone(), &default_nonce);
|
txq.add(tx3.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx.clone(), &default_nonce);
|
txq.add(tx.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().pending, 3);
|
assert_eq!(txq.status().pending, 3);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -572,8 +770,8 @@ mod test {
|
|||||||
let (tx, tx2) = new_txs(U256::one());
|
let (tx, tx2) = new_txs(U256::one());
|
||||||
|
|
||||||
// add
|
// add
|
||||||
txq.add(tx2.clone(), &default_nonce);
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx.clone(), &default_nonce);
|
txq.add(tx.clone(), &default_nonce).unwrap();
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 2);
|
assert_eq!(stats.pending, 2);
|
||||||
|
|
||||||
@ -590,11 +788,11 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::with_limits(1, 1);
|
let mut txq = TransactionQueue::with_limits(1, 1);
|
||||||
let (tx, tx2) = new_txs(U256::one());
|
let (tx, tx2) = new_txs(U256::one());
|
||||||
txq.add(tx.clone(), &default_nonce);
|
txq.add(tx.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().pending, 1);
|
assert_eq!(txq.status().pending, 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx2.clone(), &default_nonce);
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let t = txq.top_transactions(2);
|
let t = txq.top_transactions(2);
|
||||||
@ -608,14 +806,14 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::with_limits(10, 1);
|
let mut txq = TransactionQueue::with_limits(10, 1);
|
||||||
let (tx1, tx2) = new_txs(U256::from(4));
|
let (tx1, tx2) = new_txs(U256::from(4));
|
||||||
let (tx3, tx4) = new_txs(U256::from(4));
|
let (tx3, tx4) = new_txs(U256::from(4));
|
||||||
txq.add(tx1.clone(), &default_nonce);
|
txq.add(tx1.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx3.clone(), &default_nonce);
|
txq.add(tx3.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx2.clone(), &default_nonce);
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx4.clone(), &default_nonce);
|
txq.add(tx4.clone(), &default_nonce).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
@ -629,7 +827,7 @@ mod test {
|
|||||||
let fetch_last_nonce = |_a: &Address| last_nonce;
|
let fetch_last_nonce = |_a: &Address| last_nonce;
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx, &fetch_last_nonce);
|
txq.add(tx, &fetch_last_nonce).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
@ -638,19 +836,38 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_accept_same_transaction_twice() {
|
fn should_not_insert_same_transaction_twice() {
|
||||||
|
// given
|
||||||
|
let nonce = |a: &Address| default_nonce(a) + U256::one();
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let (_tx1, tx2) = new_txs(U256::from(1));
|
||||||
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
assert_eq!(txq.status().pending, 0);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx2.clone(), &nonce).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.future, 1);
|
||||||
|
assert_eq!(stats.pending, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_accept_same_transaction_twice_if_removed() {
|
||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::new();
|
||||||
let (tx1, tx2) = new_txs(U256::from(1));
|
let (tx1, tx2) = new_txs(U256::from(1));
|
||||||
txq.add(tx1.clone(), &default_nonce);
|
txq.add(tx1.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx2.clone(), &default_nonce);
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.remove(&tx1.hash(), &default_nonce);
|
txq.remove(&tx1.hash(), &default_nonce);
|
||||||
assert_eq!(txq.status().pending, 0);
|
assert_eq!(txq.status().pending, 0);
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx1.clone(), &default_nonce);
|
txq.add(tx1.clone(), &default_nonce).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
@ -665,10 +882,10 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::new();
|
||||||
let (tx, tx2) = new_txs(U256::from(1));
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
let tx3 = new_tx();
|
let tx3 = new_tx();
|
||||||
txq.add(tx2.clone(), &default_nonce);
|
txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx3.clone(), &default_nonce);
|
txq.add(tx3.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx.clone(), &default_nonce);
|
txq.add(tx.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().pending, 3);
|
assert_eq!(txq.status().pending, 3);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -680,4 +897,76 @@ mod test {
|
|||||||
assert_eq!(stats.pending, 2);
|
assert_eq!(stats.pending, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_replace_same_transaction_when_has_higher_fee() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let keypair = KeyPair::create().unwrap();
|
||||||
|
let tx = new_unsigned_tx(U256::from(123)).sign(&keypair.secret());
|
||||||
|
let tx2 = {
|
||||||
|
let mut tx2 = tx.deref().clone();
|
||||||
|
tx2.gas_price = U256::from(200);
|
||||||
|
tx2.sign(&keypair.secret())
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx, &default_nonce).unwrap();
|
||||||
|
txq.add(tx2, &default_nonce).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.pending, 1);
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
assert_eq!(txq.top_transactions(1)[0].gas_price, U256::from(200));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_replace_same_transaction_when_importing_to_futures() {
|
||||||
|
// given
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let keypair = KeyPair::create().unwrap();
|
||||||
|
let tx0 = new_unsigned_tx(U256::from(123)).sign(&keypair.secret());
|
||||||
|
let tx1 = {
|
||||||
|
let mut tx1 = tx0.deref().clone();
|
||||||
|
tx1.nonce = U256::from(124);
|
||||||
|
tx1.sign(&keypair.secret())
|
||||||
|
};
|
||||||
|
let tx2 = {
|
||||||
|
let mut tx2 = tx1.deref().clone();
|
||||||
|
tx2.gas_price = U256::from(200);
|
||||||
|
tx2.sign(&keypair.secret())
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx1, &default_nonce).unwrap();
|
||||||
|
txq.add(tx2, &default_nonce).unwrap();
|
||||||
|
assert_eq!(txq.status().future, 1);
|
||||||
|
txq.add(tx0, &default_nonce).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
assert_eq!(stats.pending, 2);
|
||||||
|
assert_eq!(txq.top_transactions(2)[1].gas_price, U256::from(200));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_recalculate_height_when_removing_from_future() {
|
||||||
|
// given
|
||||||
|
let previous_nonce = |a: &Address| default_nonce(a) - U256::one();
|
||||||
|
let next_nonce = |a: &Address| default_nonce(a) + U256::one();
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
let (tx1, tx2) = new_txs(U256::one());
|
||||||
|
txq.add(tx1.clone(), &previous_nonce).unwrap();
|
||||||
|
txq.add(tx2, &previous_nonce).unwrap();
|
||||||
|
assert_eq!(txq.status().future, 2);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.remove(&tx1.hash(), &next_nonce);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let stats = txq.status();
|
||||||
|
assert_eq!(stats.future, 0);
|
||||||
|
assert_eq!(stats.pending, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ chrono = "0.2"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
dev = ["clippy"]
|
dev = []
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
vergen = "*"
|
vergen = "*"
|
||||||
|
@ -1103,7 +1103,7 @@ macro_rules! construct_uint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(derive_hash_xor_eq))] // We are pretty sure it's ok.
|
#[cfg_attr(all(nightly, feature="dev"), allow(derive_hash_xor_eq))] // We are pretty sure it's ok.
|
||||||
impl Hash for $name {
|
impl Hash for $name {
|
||||||
fn hash<H>(&self, state: &mut H) where H: Hasher {
|
fn hash<H>(&self, state: &mut H) where H: Hasher {
|
||||||
unsafe { state.write(::std::slice::from_raw_parts(self.0.as_ptr() as *mut u8, self.0.len() * 8)); }
|
unsafe { state.write(::std::slice::from_raw_parts(self.0.as_ptr() as *mut u8, self.0.len() * 8)); }
|
||||||
@ -1485,7 +1485,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature="dev", allow(eq_op))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(eq_op))]
|
||||||
pub fn uint256_comp_test() {
|
pub fn uint256_comp_test() {
|
||||||
let small = U256([10u64, 0, 0, 0]);
|
let small = U256([10u64, 0, 0, 0]);
|
||||||
let big = U256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
let big = U256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||||
@ -2032,7 +2032,7 @@ mod tests {
|
|||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature = "dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
|
||||||
fn u256_multi_full_mul() {
|
fn u256_multi_full_mul() {
|
||||||
let result = U256([0, 0, 0, 0]).full_mul(U256([0, 0, 0, 0]));
|
let result = U256([0, 0, 0, 0]).full_mul(U256([0, 0, 0, 0]));
|
||||||
assert_eq!(U512([0, 0, 0, 0, 0, 0, 0, 0]), result);
|
assert_eq!(U512([0, 0, 0, 0, 0, 0, 0, 0]), result);
|
||||||
|
@ -1,7 +1,28 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate rustc_version;
|
||||||
extern crate vergen;
|
extern crate vergen;
|
||||||
|
|
||||||
use vergen::*;
|
use vergen::*;
|
||||||
|
use rustc_version::{version_meta, Channel};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
vergen(OutputFns::all()).unwrap();
|
vergen(OutputFns::all()).unwrap();
|
||||||
|
if let Channel::Nightly = version_meta().channel {
|
||||||
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,7 +305,7 @@ macro_rules! impl_hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Copy for $from {}
|
impl Copy for $from {}
|
||||||
#[cfg_attr(feature="dev", allow(expl_impl_clone_on_copy))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(expl_impl_clone_on_copy))]
|
||||||
impl Clone for $from {
|
impl Clone for $from {
|
||||||
fn clone(&self) -> $from {
|
fn clone(&self) -> $from {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -637,7 +637,7 @@ mod tests {
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature="dev", allow(eq_op))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(eq_op))]
|
||||||
fn hash() {
|
fn hash() {
|
||||||
let h = H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
|
let h = H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
|
||||||
assert_eq!(H64::from_str("0123456789abcdef").unwrap(), h);
|
assert_eq!(H64::from_str("0123456789abcdef").unwrap(), h);
|
||||||
|
@ -153,7 +153,7 @@ struct UserTimer {
|
|||||||
pub struct IoManager<Message> where Message: Send + Sync {
|
pub struct IoManager<Message> where Message: Send + Sync {
|
||||||
timers: Arc<RwLock<HashMap<HandlerId, UserTimer>>>,
|
timers: Arc<RwLock<HashMap<HandlerId, UserTimer>>>,
|
||||||
handlers: Vec<Arc<IoHandler<Message>>>,
|
handlers: Vec<Arc<IoHandler<Message>>>,
|
||||||
_workers: Vec<Worker>,
|
workers: Vec<Worker>,
|
||||||
worker_channel: chase_lev::Worker<Work<Message>>,
|
worker_channel: chase_lev::Worker<Work<Message>>,
|
||||||
work_ready: Arc<Condvar>,
|
work_ready: Arc<Condvar>,
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ impl<Message> IoManager<Message> where Message: Send + Sync + Clone + 'static {
|
|||||||
timers: Arc::new(RwLock::new(HashMap::new())),
|
timers: Arc::new(RwLock::new(HashMap::new())),
|
||||||
handlers: Vec::new(),
|
handlers: Vec::new(),
|
||||||
worker_channel: worker,
|
worker_channel: worker,
|
||||||
_workers: workers,
|
workers: workers,
|
||||||
work_ready: work_ready,
|
work_ready: work_ready,
|
||||||
};
|
};
|
||||||
try!(event_loop.run(&mut io));
|
try!(event_loop.run(&mut io));
|
||||||
@ -230,7 +230,10 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
|||||||
|
|
||||||
fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
|
fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
|
||||||
match msg {
|
match msg {
|
||||||
IoMessage::Shutdown => event_loop.shutdown(),
|
IoMessage::Shutdown => {
|
||||||
|
self.workers.clear();
|
||||||
|
event_loop.shutdown();
|
||||||
|
},
|
||||||
IoMessage::AddHandler { handler } => {
|
IoMessage::AddHandler { handler } => {
|
||||||
let handler_id = {
|
let handler_id = {
|
||||||
self.handlers.push(handler.clone());
|
self.handlers.push(handler.clone());
|
||||||
|
@ -25,10 +25,7 @@ use kvdb::{Database, DBTransaction, DatabaseConfig};
|
|||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
/// 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
|
||||||
/// and, possibly, latent-removal semantics.
|
/// and latent-removal semantics.
|
||||||
///
|
|
||||||
/// If `counters` is `None`, then it behaves exactly like OverlayDB. If not it behaves
|
|
||||||
/// differently:
|
|
||||||
///
|
///
|
||||||
/// Like OverlayDB, there is a memory overlay; `commit()` must be called in order to
|
/// Like OverlayDB, there is a memory overlay; `commit()` must be called in order to
|
||||||
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
/// write operations out to disk. Unlike OverlayDB, `remove()` operations do not take effect
|
||||||
@ -60,7 +57,6 @@ const DB_VERSION_NO_JOURNAL : u32 = 3 + 256;
|
|||||||
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
const PADDING : [u8; 10] = [ 0u8; 10 ];
|
||||||
|
|
||||||
impl JournalDB {
|
impl JournalDB {
|
||||||
|
|
||||||
/// Create a new instance from file
|
/// Create a new instance from file
|
||||||
pub fn new(path: &str) -> JournalDB {
|
pub fn new(path: &str) -> JournalDB {
|
||||||
Self::from_prefs(path, true)
|
Self::from_prefs(path, true)
|
||||||
@ -149,6 +145,87 @@ impl JournalDB {
|
|||||||
Ok(ret as u32)
|
Ok(ret as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn morph_key(key: &H256, index: u8) -> Bytes {
|
||||||
|
let mut ret = key.bytes().to_owned();
|
||||||
|
ret.push(index);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next three are valid only as long as there is an insert operation of `key` in the journal.
|
||||||
|
fn set_already_in(batch: &DBTransaction, key: &H256) { batch.put(&Self::morph_key(key, 0), &[1u8]).expect("Low-level database error. Some issue with your hard disk?"); }
|
||||||
|
fn reset_already_in(batch: &DBTransaction, key: &H256) { batch.delete(&Self::morph_key(key, 0)).expect("Low-level database error. Some issue with your hard disk?"); }
|
||||||
|
fn is_already_in(backing: &Database, key: &H256) -> bool {
|
||||||
|
backing.get(&Self::morph_key(key, 0)).expect("Low-level database error. Some issue with your hard disk?").is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_keys(inserts: &[(H256, Bytes)], backing: &Database, counters: &mut HashMap<H256, i32>, batch: &DBTransaction) {
|
||||||
|
for &(ref h, ref d) in inserts {
|
||||||
|
if let Some(c) = counters.get_mut(h) {
|
||||||
|
// already counting. increment.
|
||||||
|
*c += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the first entry for this node in the journal.
|
||||||
|
if backing.get(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?").is_some() {
|
||||||
|
// already in the backing DB. start counting, and remember it was already in.
|
||||||
|
Self::set_already_in(batch, &h);
|
||||||
|
counters.insert(h.clone(), 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
assert!(!Self::is_already_in(backing, &h));
|
||||||
|
batch.put(&h.bytes(), d).expect("Low-level database error. Some issue with your hard disk?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replay_keys(inserts: &[H256], backing: &Database, counters: &mut HashMap<H256, i32>) {
|
||||||
|
trace!("replay_keys: inserts={:?}, counters={:?}", inserts, counters);
|
||||||
|
for h in inserts {
|
||||||
|
if let Some(c) = counters.get_mut(h) {
|
||||||
|
// already counting. increment.
|
||||||
|
*c += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the first entry for this node in the journal.
|
||||||
|
// it is initialised to 1 if it was already in.
|
||||||
|
if Self::is_already_in(backing, h) {
|
||||||
|
trace!("replace_keys: Key {} was already in!", h);
|
||||||
|
counters.insert(h.clone(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace!("replay_keys: (end) counters={:?}", counters);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill_keys(deletes: Vec<H256>, counters: &mut HashMap<H256, i32>, batch: &DBTransaction) {
|
||||||
|
for h in deletes.into_iter() {
|
||||||
|
let mut n: Option<i32> = None;
|
||||||
|
if let Some(c) = counters.get_mut(&h) {
|
||||||
|
if *c > 1 {
|
||||||
|
*c -= 1;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
n = Some(*c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match n {
|
||||||
|
Some(i) if i == 1 => {
|
||||||
|
counters.remove(&h);
|
||||||
|
Self::reset_already_in(batch, &h);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// 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));
|
||||||
|
batch.delete(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?");
|
||||||
|
}
|
||||||
|
_ => panic!("Invalid value in counters: {:?}", n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Commit all recent insert operations and historical removals from the old era
|
/// Commit all recent insert operations and historical removals from the old era
|
||||||
/// to the backing database.
|
/// to the backing database.
|
||||||
fn commit_with_counters(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
fn commit_with_counters(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
|
||||||
@ -159,21 +236,42 @@ impl JournalDB {
|
|||||||
|
|
||||||
// TODO: store reclaim_period.
|
// TODO: store reclaim_period.
|
||||||
|
|
||||||
// when we make a new commit, we journal the inserts and removes.
|
// When we make a new commit, we make a journal of all blocks in the recent history and record
|
||||||
// for each end_era that we journaled that we are no passing by,
|
// all keys that were inserted and deleted. The journal is ordered by era; multiple commits can
|
||||||
// we remove all of its removes assuming it is canonical and all
|
// share the same era. This forms a data structure similar to a queue but whose items are tuples.
|
||||||
// of its inserts otherwise.
|
// By the time comes to remove a tuple from the queue (i.e. then the era passes from recent history
|
||||||
|
// into ancient history) then only one commit from the tuple is considered canonical. This commit
|
||||||
|
// is kept in the main backing database, whereas any others from the same era are reverted.
|
||||||
//
|
//
|
||||||
// We also keep reference counters for each key inserted in the journal to handle
|
// It is possible that a key, properly available in the backing database be deleted and re-inserted
|
||||||
// the following cases where key K must not be deleted from the DB when processing removals :
|
// in the recent history queue, yet have both operations in commits that are eventually non-canonical.
|
||||||
// Given H is the journal size in eras, 0 <= C <= H.
|
// To avoid the original, and still required, key from being deleted, we maintain a reference count
|
||||||
// Key K is removed in era A(N) and re-inserted in canonical era B(N + C).
|
// which includes an original key, if any.
|
||||||
// Key K is removed in era A(N) and re-inserted in non-canonical era B`(N + C).
|
//
|
||||||
// Key K is added in non-canonical era A'(N) canonical B(N + C).
|
// The semantics of the `counter` are:
|
||||||
|
// insert key k:
|
||||||
|
// counter already contains k: count += 1
|
||||||
|
// counter doesn't contain k:
|
||||||
|
// backing db contains k: count = 1
|
||||||
|
// backing db doesn't contain k: insert into backing db, count = 0
|
||||||
|
// delete key k:
|
||||||
|
// counter contains k (count is asserted to be non-zero):
|
||||||
|
// count > 1: counter -= 1
|
||||||
|
// count == 1: remove counter
|
||||||
|
// count == 0: remove key from backing db
|
||||||
|
// counter doesn't contain k: remove key from backing db
|
||||||
|
//
|
||||||
|
// Practically, this means that for each commit block turning from recent to ancient we do the
|
||||||
|
// following:
|
||||||
|
// is_canonical:
|
||||||
|
// inserts: Ignored (left alone in the backing database).
|
||||||
|
// deletes: Enacted; however, recent history queue is checked for ongoing references. This is
|
||||||
|
// reduced as a preference to deletion from the backing database.
|
||||||
|
// !is_canonical:
|
||||||
|
// inserts: Reverted; however, recent history queue is checked for ongoing references. This is
|
||||||
|
// reduced as a preference to deletion from the backing database.
|
||||||
|
// deletes: Ignored (they were never inserted).
|
||||||
//
|
//
|
||||||
// The counter is encreased each time a key is inserted in the journal in the commit. The list of insertions
|
|
||||||
// is saved with the era record. When the era becomes end_era and goes out of journal the counter is decreased
|
|
||||||
// and the key is safe to delete.
|
|
||||||
|
|
||||||
// record new commit's details.
|
// record new commit's details.
|
||||||
trace!("commit: #{} ({}), end era: {:?}", now, id, end);
|
trace!("commit: #{} ({}), end era: {:?}", now, id, end);
|
||||||
@ -183,36 +281,40 @@ impl JournalDB {
|
|||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
let mut last;
|
let mut last;
|
||||||
|
|
||||||
while {
|
while try!(self.backing.get({
|
||||||
let record = try!(self.backing.get({
|
|
||||||
let mut r = RlpStream::new_list(3);
|
let mut r = RlpStream::new_list(3);
|
||||||
r.append(&now);
|
r.append(&now);
|
||||||
r.append(&index);
|
r.append(&index);
|
||||||
r.append(&&PADDING[..]);
|
r.append(&&PADDING[..]);
|
||||||
last = r.drain();
|
last = r.drain();
|
||||||
&last
|
&last
|
||||||
}));
|
})).is_some() {
|
||||||
match record {
|
|
||||||
Some(r) => {
|
|
||||||
assert!(&Rlp::new(&r).val_at::<H256>(0) != id);
|
|
||||||
true
|
|
||||||
},
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
} {
|
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let drained = self.overlay.drain();
|
||||||
|
let removes: Vec<H256> = drained
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(k, &(_, c))| if c < 0 {Some(k.clone())} else {None})
|
||||||
|
.collect();
|
||||||
|
let inserts: Vec<(H256, Bytes)> = drained
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(k, (v, r))| if r > 0 { assert!(r == 1); Some((k, v)) } else { assert!(r >= -1); None })
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut r = RlpStream::new_list(3);
|
let mut r = RlpStream::new_list(3);
|
||||||
let inserts: Vec<H256> = self.overlay.keys().iter().filter(|&(_, &c)| c > 0).map(|(key, _)| key.clone()).collect();
|
|
||||||
// Increase counter for each inserted key no matter if the block is canonical or not.
|
|
||||||
for i in &inserts {
|
|
||||||
*counters.entry(i.clone()).or_insert(0) += 1;
|
|
||||||
}
|
|
||||||
let removes: Vec<H256> = self.overlay.keys().iter().filter(|&(_, &c)| c < 0).map(|(key, _)| key.clone()).collect();
|
|
||||||
r.append(id);
|
r.append(id);
|
||||||
r.append(&inserts);
|
|
||||||
|
// Process the new inserts.
|
||||||
|
// We use the inserts for three things. For each:
|
||||||
|
// - we place into the backing DB or increment the counter if already in;
|
||||||
|
// - we note in the backing db that it was already in;
|
||||||
|
// - we write the key into our journal for this block;
|
||||||
|
|
||||||
|
r.begin_list(inserts.len());
|
||||||
|
inserts.iter().foreach(|&(k, _)| {r.append(&k);});
|
||||||
r.append(&removes);
|
r.append(&removes);
|
||||||
|
Self::insert_keys(&inserts, &self.backing, &mut counters, &batch);
|
||||||
try!(batch.put(&last, r.as_raw()));
|
try!(batch.put(&last, r.as_raw()));
|
||||||
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
try!(batch.put(&LATEST_ERA_KEY, &encode(&now)));
|
||||||
}
|
}
|
||||||
@ -221,8 +323,6 @@ impl JournalDB {
|
|||||||
if let Some((end_era, canon_id)) = end {
|
if let Some((end_era, canon_id)) = end {
|
||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
let mut last;
|
let mut last;
|
||||||
let mut to_remove: Vec<H256> = Vec::new();
|
|
||||||
let mut canon_inserts: Vec<H256> = Vec::new();
|
|
||||||
while let Some(rlp_data) = try!(self.backing.get({
|
while let Some(rlp_data) = try!(self.backing.get({
|
||||||
let mut r = RlpStream::new_list(3);
|
let mut r = RlpStream::new_list(3);
|
||||||
r.append(&end_era);
|
r.append(&end_era);
|
||||||
@ -232,54 +332,19 @@ impl JournalDB {
|
|||||||
&last
|
&last
|
||||||
})) {
|
})) {
|
||||||
let rlp = Rlp::new(&rlp_data);
|
let rlp = Rlp::new(&rlp_data);
|
||||||
let mut inserts: Vec<H256> = rlp.val_at(1);
|
let inserts: Vec<H256> = rlp.val_at(1);
|
||||||
JournalDB::decrease_counters(&inserts, &mut counters);
|
let deletes: Vec<H256> = rlp.val_at(2);
|
||||||
// Collect keys to be removed. These are removed keys for canonical block, inserted for non-canonical
|
// Collect keys to be removed. These are removed keys for canonical block, inserted for non-canonical
|
||||||
if canon_id == rlp.val_at(0) {
|
Self::kill_keys(if canon_id == rlp.val_at(0) {deletes} else {inserts}, &mut counters, &batch);
|
||||||
let mut canon_deletes: Vec<H256> = rlp.val_at(2);
|
|
||||||
trace!("Purging nodes deleted from canon: {:?}", canon_deletes);
|
|
||||||
to_remove.append(&mut canon_deletes);
|
|
||||||
canon_inserts = inserts;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
trace!("Purging nodes inserted in non-canon: {:?}", inserts);
|
|
||||||
to_remove.append(&mut inserts);
|
|
||||||
}
|
|
||||||
trace!("commit: Delete journal for time #{}.{}: {}, (canon was {}): {} entries", end_era, index, rlp.val_at::<H256>(0), canon_id, to_remove.len());
|
|
||||||
try!(batch.delete(&last));
|
try!(batch.delete(&last));
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
trace!("JournalDB: delete journal for time #{}.{}, (canon was {})", end_era, index, canon_id);
|
||||||
let canon_inserts = canon_inserts.drain(..).collect::<HashSet<_>>();
|
|
||||||
// Purge removed keys if they are not referenced and not re-inserted in the canon commit
|
|
||||||
let mut deletes = 0;
|
|
||||||
trace!("Purging filtered nodes: {:?}", to_remove.iter().filter(|h| !counters.contains_key(h) && !canon_inserts.contains(h)).collect::<Vec<_>>());
|
|
||||||
for h in to_remove.iter().filter(|h| !counters.contains_key(h) && !canon_inserts.contains(h)) {
|
|
||||||
try!(batch.delete(&h));
|
|
||||||
deletes += 1;
|
|
||||||
}
|
|
||||||
trace!("Total nodes purged: {}", deletes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit overlay insertions
|
|
||||||
let ret = Self::batch_overlay_insertions(&mut self.overlay, &batch);
|
|
||||||
try!(self.backing.write(batch));
|
try!(self.backing.write(batch));
|
||||||
Ok(ret as u32)
|
// trace!("JournalDB::commit() deleted {} nodes", deletes);
|
||||||
}
|
Ok(0)
|
||||||
|
|
||||||
|
|
||||||
// Decrease counters for given keys. Deletes obsolete counters
|
|
||||||
fn decrease_counters(keys: &[H256], counters: &mut HashMap<H256, i32>) {
|
|
||||||
for i in keys.iter() {
|
|
||||||
let delete_counter = {
|
|
||||||
let cnt = counters.get_mut(i).expect("Missing key counter");
|
|
||||||
*cnt -= 1;
|
|
||||||
*cnt == 0
|
|
||||||
};
|
|
||||||
if delete_counter {
|
|
||||||
counters.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn payload(&self, key: &H256) -> Option<Bytes> {
|
fn payload(&self, key: &H256) -> Option<Bytes> {
|
||||||
@ -287,7 +352,7 @@ impl JournalDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_counters(db: &Database) -> HashMap<H256, i32> {
|
fn read_counters(db: &Database) -> HashMap<H256, i32> {
|
||||||
let mut res = HashMap::new();
|
let mut counters = HashMap::new();
|
||||||
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
if let Some(val) = db.get(&LATEST_ERA_KEY).expect("Low-level database error.") {
|
||||||
let mut era = decode::<u64>(&val);
|
let mut era = decode::<u64>(&val);
|
||||||
loop {
|
loop {
|
||||||
@ -299,11 +364,10 @@ impl JournalDB {
|
|||||||
r.append(&&PADDING[..]);
|
r.append(&&PADDING[..]);
|
||||||
&r.drain()
|
&r.drain()
|
||||||
}).expect("Low-level database error.") {
|
}).expect("Low-level database error.") {
|
||||||
|
trace!("read_counters: era={}, index={}", era, index);
|
||||||
let rlp = Rlp::new(&rlp_data);
|
let rlp = Rlp::new(&rlp_data);
|
||||||
let to_add: Vec<H256> = rlp.val_at(1);
|
let inserts: Vec<H256> = rlp.val_at(1);
|
||||||
for h in to_add {
|
Self::replay_keys(&inserts, db, &mut counters);
|
||||||
*res.entry(h).or_insert(0) += 1;
|
|
||||||
}
|
|
||||||
index += 1;
|
index += 1;
|
||||||
};
|
};
|
||||||
if index == 0 || era == 0 {
|
if index == 0 || era == 0 {
|
||||||
@ -312,8 +376,16 @@ impl JournalDB {
|
|||||||
era -= 1;
|
era -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace!("Recovered {} counters", res.len());
|
trace!("Recovered {} counters", counters.len());
|
||||||
res
|
counters
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns heap memory size used
|
||||||
|
pub fn mem_used(&self) -> usize {
|
||||||
|
self.overlay.mem_used() + match self.counters {
|
||||||
|
Some(ref c) => c.read().unwrap().heap_size_of_children(),
|
||||||
|
None => 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,6 +440,28 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use hashdb::*;
|
use hashdb::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_same_in_fork() {
|
||||||
|
// history is 1
|
||||||
|
let mut jdb = JournalDB::new_temp();
|
||||||
|
|
||||||
|
let x = jdb.insert(b"X");
|
||||||
|
jdb.commit(1, &b"1".sha3(), None).unwrap();
|
||||||
|
jdb.commit(2, &b"2".sha3(), None).unwrap();
|
||||||
|
jdb.commit(3, &b"1002a".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
jdb.commit(4, &b"1003a".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.remove(&x);
|
||||||
|
jdb.commit(3, &b"1002b".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
let x = jdb.insert(b"X");
|
||||||
|
jdb.commit(4, &b"1003b".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.commit(5, &b"1004a".sha3(), Some((3, b"1002a".sha3()))).unwrap();
|
||||||
|
jdb.commit(6, &b"1005a".sha3(), Some((4, b"1003a".sha3()))).unwrap();
|
||||||
|
|
||||||
|
assert!(jdb.exists(&x));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn long_history() {
|
fn long_history() {
|
||||||
// history is 3
|
// history is 3
|
||||||
@ -488,15 +582,18 @@ mod tests {
|
|||||||
assert!(jdb.exists(&foo));
|
assert!(jdb.exists(&foo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reopen() {
|
fn reopen() {
|
||||||
let mut dir = ::std::env::temp_dir();
|
let mut dir = ::std::env::temp_dir();
|
||||||
dir.push(H32::random().hex());
|
dir.push(H32::random().hex());
|
||||||
|
let bar = H256::random();
|
||||||
|
|
||||||
let foo = {
|
let foo = {
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
// history is 1
|
// history is 1
|
||||||
let foo = jdb.insert(b"foo");
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.emplace(bar.clone(), b"bar".to_vec());
|
||||||
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
foo
|
foo
|
||||||
};
|
};
|
||||||
@ -510,8 +607,67 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
assert!(jdb.exists(&foo));
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(jdb.exists(&bar));
|
||||||
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
assert!(!jdb.exists(&foo));
|
assert!(!jdb.exists(&foo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reopen_remove() {
|
||||||
|
let mut dir = ::std::env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
|
||||||
|
let foo = {
|
||||||
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
|
// history is 1
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
jdb.commit(1, &b"1".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
// foo is ancient history.
|
||||||
|
|
||||||
|
jdb.insert(b"foo");
|
||||||
|
jdb.commit(2, &b"2".sha3(), Some((1, b"1".sha3()))).unwrap();
|
||||||
|
foo
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(3, &b"3".sha3(), Some((2, b"2".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
jdb.remove(&foo);
|
||||||
|
jdb.commit(4, &b"4".sha3(), Some((3, b"3".sha3()))).unwrap();
|
||||||
|
jdb.commit(5, &b"5".sha3(), Some((4, b"4".sha3()))).unwrap();
|
||||||
|
assert!(!jdb.exists(&foo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn reopen_fork() {
|
||||||
|
let mut dir = ::std::env::temp_dir();
|
||||||
|
dir.push(H32::random().hex());
|
||||||
|
let (foo, bar, baz) = {
|
||||||
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
|
// history is 1
|
||||||
|
let foo = jdb.insert(b"foo");
|
||||||
|
let bar = jdb.insert(b"bar");
|
||||||
|
jdb.commit(0, &b"0".sha3(), None).unwrap();
|
||||||
|
jdb.remove(&foo);
|
||||||
|
let baz = jdb.insert(b"baz");
|
||||||
|
jdb.commit(1, &b"1a".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
|
||||||
|
jdb.remove(&bar);
|
||||||
|
jdb.commit(1, &b"1b".sha3(), Some((0, b"0".sha3()))).unwrap();
|
||||||
|
(foo, bar, baz)
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut jdb = JournalDB::new(dir.to_str().unwrap());
|
||||||
|
jdb.commit(2, &b"2b".sha3(), Some((1, b"1b".sha3()))).unwrap();
|
||||||
|
assert!(jdb.exists(&foo));
|
||||||
|
assert!(!jdb.exists(&baz));
|
||||||
|
assert!(!jdb.exists(&bar));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ impl SecretStore {
|
|||||||
let mut path = ::std::env::home_dir().expect("Failed to get home dir");
|
let mut path = ::std::env::home_dir().expect("Failed to get home dir");
|
||||||
path.push(".parity");
|
path.push(".parity");
|
||||||
path.push("keys");
|
path.push("keys");
|
||||||
|
::std::fs::create_dir_all(&path).expect("Should panic since it is critical to be able to access home dir");
|
||||||
Self::new_in(&path)
|
Self::new_in(&path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,8 +55,7 @@ pub struct DatabaseIterator<'a> {
|
|||||||
impl<'a> Iterator for DatabaseIterator<'a> {
|
impl<'a> Iterator for DatabaseIterator<'a> {
|
||||||
type Item = (Box<[u8]>, Box<[u8]>);
|
type Item = (Box<[u8]>, Box<[u8]>);
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(type_complexity))]
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
fn next(&mut self) -> Option<(Box<[u8]>, Box<[u8]>)> {
|
|
||||||
self.iter.next()
|
self.iter.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,18 +15,18 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![cfg_attr(feature="dev", feature(plugin))]
|
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
|
||||||
#![cfg_attr(feature="dev", plugin(clippy))]
|
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
|
||||||
|
|
||||||
// Clippy settings
|
// Clippy settings
|
||||||
// TODO [todr] not really sure
|
// TODO [todr] not really sure
|
||||||
#![cfg_attr(feature="dev", allow(needless_range_loop))]
|
#![cfg_attr(all(nightly, feature="dev"), allow(needless_range_loop))]
|
||||||
// Shorter than if-else
|
// Shorter than if-else
|
||||||
#![cfg_attr(feature="dev", allow(match_bool))]
|
#![cfg_attr(all(nightly, feature="dev"), allow(match_bool))]
|
||||||
// We use that to be more explicit about handled cases
|
// We use that to be more explicit about handled cases
|
||||||
#![cfg_attr(feature="dev", allow(match_same_arms))]
|
#![cfg_attr(all(nightly, feature="dev"), allow(match_same_arms))]
|
||||||
// Keeps consistency (all lines with `.clone()`) and helpful when changing ref to non-ref.
|
// Keeps consistency (all lines with `.clone()`) and helpful when changing ref to non-ref.
|
||||||
#![cfg_attr(feature="dev", allow(clone_on_copy))]
|
#![cfg_attr(all(nightly, feature="dev"), allow(clone_on_copy))]
|
||||||
|
|
||||||
//! Ethcore-util library
|
//! Ethcore-util library
|
||||||
//!
|
//!
|
||||||
|
@ -21,6 +21,7 @@ use bytes::*;
|
|||||||
use rlp::*;
|
use rlp::*;
|
||||||
use sha3::*;
|
use sha3::*;
|
||||||
use hashdb::*;
|
use hashdb::*;
|
||||||
|
use heapsize::*;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -143,6 +144,11 @@ impl MemoryDB {
|
|||||||
}
|
}
|
||||||
self.raw(key).unwrap()
|
self.raw(key).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the size of allocated heap memory
|
||||||
|
pub fn mem_used(&self) -> usize {
|
||||||
|
self.data.heap_size_of_children()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];
|
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];
|
||||||
|
@ -243,7 +243,7 @@ impl Discovery {
|
|||||||
self.send_to(packet, address.clone());
|
self.send_to(packet, address.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(map_clone))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(map_clone))]
|
||||||
fn nearest_node_entries(target: &NodeId, buckets: &[NodeBucket]) -> Vec<NodeEntry> {
|
fn nearest_node_entries(target: &NodeId, buckets: &[NodeBucket]) -> Vec<NodeEntry> {
|
||||||
let mut found: BTreeMap<u32, Vec<&NodeEntry>> = BTreeMap::new();
|
let mut found: BTreeMap<u32, Vec<&NodeEntry>> = BTreeMap::new();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
@ -507,7 +507,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
debug!(target: "network", "Connecting peers: {} sessions, {} pending", self.session_count(), self.handshake_count());
|
debug!(target: "network", "Connecting peers: {} sessions, {} pending", self.session_count(), self.handshake_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(single_match))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(single_match))]
|
||||||
fn connect_peer(&self, id: &NodeId, io: &IoContext<NetworkIoMessage<Message>>) {
|
fn connect_peer(&self, id: &NodeId, io: &IoContext<NetworkIoMessage<Message>>) {
|
||||||
if self.have_session(id)
|
if self.have_session(id)
|
||||||
{
|
{
|
||||||
@ -542,7 +542,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
|||||||
self.create_connection(socket, Some(id), io);
|
self.create_connection(socket, Some(id), io);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(block_in_if_condition_stmt))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(block_in_if_condition_stmt))]
|
||||||
fn create_connection(&self, socket: TcpStream, id: Option<&NodeId>, io: &IoContext<NetworkIoMessage<Message>>) {
|
fn create_connection(&self, socket: TcpStream, id: Option<&NodeId>, io: &IoContext<NetworkIoMessage<Message>>) {
|
||||||
let nonce = self.info.write().unwrap().next_nonce();
|
let nonce = self.info.write().unwrap().next_nonce();
|
||||||
let mut handshakes = self.handshakes.write().unwrap();
|
let mut handshakes = self.handshakes.write().unwrap();
|
||||||
|
@ -71,7 +71,7 @@ impl PanicHandler {
|
|||||||
|
|
||||||
/// Invoke closure and catch any possible panics.
|
/// Invoke closure and catch any possible panics.
|
||||||
/// In case of panic notifies all listeners about it.
|
/// In case of panic notifies all listeners about it.
|
||||||
#[cfg_attr(feature="dev", allow(deprecated))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(deprecated))]
|
||||||
pub fn catch_panic<G, R>(&self, g: G) -> thread::Result<R> where G: FnOnce() -> R + Send + 'static {
|
pub fn catch_panic<G, R>(&self, g: G) -> thread::Result<R> where G: FnOnce() -> R + Send + 'static {
|
||||||
let _guard = PanicGuard { handler: self };
|
let _guard = PanicGuard { handler: self };
|
||||||
let result = g();
|
let result = g();
|
||||||
|
@ -54,7 +54,7 @@ pub struct TrieDB<'db> {
|
|||||||
pub hash_count: usize,
|
pub hash_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(wrong_self_convention))]
|
||||||
impl<'db> TrieDB<'db> {
|
impl<'db> TrieDB<'db> {
|
||||||
/// Create a new trie with the backing database `db` and `root`
|
/// Create a new trie with the backing database `db` and `root`
|
||||||
/// Panics, if `root` does not exist
|
/// Panics, if `root` does not exist
|
||||||
|
@ -66,7 +66,7 @@ enum MaybeChanged<'a> {
|
|||||||
Changed(Bytes),
|
Changed(Bytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(wrong_self_convention))]
|
||||||
impl<'db> TrieDBMut<'db> {
|
impl<'db> TrieDBMut<'db> {
|
||||||
/// Create a new trie with the backing database `db` and empty `root`
|
/// Create a new trie with the backing database `db` and empty `root`
|
||||||
/// Initialise to the state entailed by the genesis block.
|
/// Initialise to the state entailed by the genesis block.
|
||||||
@ -350,7 +350,7 @@ impl<'db> TrieDBMut<'db> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
|
||||||
/// Determine the RLP of the node, assuming we're inserting `partial` into the
|
/// Determine the RLP of the node, assuming we're inserting `partial` into the
|
||||||
/// node currently of data `old`. This will *not* delete any hash of `old` from the database;
|
/// node currently of data `old`. This will *not* delete any hash of `old` from the database;
|
||||||
/// it will just return the new RLP that includes the new node.
|
/// it will just return the new RLP that includes the new node.
|
||||||
|
Loading…
Reference in New Issue
Block a user