Merge branch 'master' of github.com:ethcore/parity into jdb_option2

This commit is contained in:
arkpar 2016-03-10 21:06:21 +01:00
commit 8785bd37e8
55 changed files with 1342 additions and 430 deletions

53
Cargo.lock generated
View File

@ -15,7 +15,9 @@ dependencies = [
"fdlimit 0.1.0",
"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)",
"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_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)",
]
@ -146,6 +148,14 @@ dependencies = [
"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]]
name = "docopt"
version = "0.6.78"
@ -209,6 +219,7 @@ dependencies = [
"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)",
"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)",
]
@ -228,10 +239,11 @@ dependencies = [
"ethcore 0.9.99",
"ethcore-util 0.9.99",
"ethsync 0.9.99",
"jsonrpc-core 1.2.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-core 2.0.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)",
"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_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)",
@ -285,7 +297,9 @@ dependencies = [
"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)",
"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_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)",
]
@ -400,7 +414,7 @@ dependencies = [
[[package]]
name = "jsonrpc-core"
version = "1.2.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -411,11 +425,11 @@ dependencies = [
[[package]]
name = "jsonrpc-http-server"
version = "2.1.0"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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)",
]
@ -655,6 +669,16 @@ dependencies = [
"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]]
name = "regex"
version = "0.1.54"
@ -680,6 +704,17 @@ dependencies = [
"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]]
name = "rust-crypto"
version = "0.2.34"
@ -813,6 +848,14 @@ dependencies = [
"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]]
name = "time"
version = "0.1.34"

View File

@ -4,6 +4,10 @@ name = "parity"
version = "0.9.99"
license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[build-dependencies]
rustc_version = "0.1"
[dependencies]
log = "0.3"
@ -12,22 +16,30 @@ rustc-serialize = "0.3"
docopt = "0.6"
time = "0.1"
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" }
daemonize = "0.2"
ethcore-devtools = { path = "devtools" }
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]
default = ["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-nightly = ["ethcore/json-tests", "dev"]
travis-nightly = ["ethcore/json-tests", "dev-clippy", "dev"]
[[bin]]
path = "parity/main.rs"

25
build.rs Normal file
View 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");
}
}

2
cargo.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
cargo "$@" --features dev-clippy

View File

@ -5,6 +5,10 @@ license = "GPL-3.0"
name = "ethcore"
version = "0.9.99"
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[build-dependencies]
rustc_version = "0.1"
[dependencies]
log = "0.3"
@ -27,5 +31,5 @@ jit = ["evmjit"]
evm-debug = []
json-tests = []
test-heavy = []
dev = ["clippy"]
dev = []
default = []

25
ethcore/build.rs Normal file
View 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");
}
}

View File

@ -24,7 +24,7 @@ pub type LogBloom = H2048;
/// Constant 2048-bit datum for 0. Often used as a default.
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.
pub enum Seal {
/// The seal/signature is included.

View File

@ -16,7 +16,7 @@
//! 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 engine::*;
@ -274,7 +274,7 @@ impl<'x> OpenBlock<'x> {
s.block.base.header.note_dirty();
ClosedBlock {
block: s.block,
block: s.block,
uncle_bytes: uncle_bytes,
}
}

View File

@ -121,7 +121,7 @@ struct QueueSignal {
}
impl QueueSignal {
#[cfg_attr(feature="dev", allow(bool_comparison))]
#[cfg_attr(all(nightly, feature="dev"), allow(bool_comparison))]
fn set(&self) {
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message");
@ -320,6 +320,9 @@ impl BlockQueue {
/// Mark given block and all its children as bad. Stops verification.
pub fn mark_as_bad(&mut self, block_hashes: &[H256]) {
if block_hashes.is_empty() {
return;
}
let mut verification_lock = self.verification.lock().unwrap();
let mut processing = self.processing.write().unwrap();
@ -345,6 +348,9 @@ impl BlockQueue {
/// Mark given block as processed
pub fn mark_as_good(&mut self, block_hashes: &[H256]) {
if block_hashes.is_empty() {
return;
}
let mut processing = self.processing.write().unwrap();
for hash in block_hashes {
processing.remove(&hash);
@ -385,7 +391,7 @@ impl BlockQueue {
}
}
pub fn collect_garbage(&self) {
pub fn collect_garbage(&self) {
{
let mut verification = self.verification.lock().unwrap();
verification.unverified.shrink_to_fit();

View File

@ -18,6 +18,7 @@ use util::numbers::{U256,H256};
use header::BlockNumber;
/// Brief info about inserted block.
#[derive(Clone)]
pub struct BlockInfo {
/// Block hash.
pub hash: H256,
@ -30,6 +31,7 @@ pub struct BlockInfo {
}
/// Describes location of newly inserted block.
#[derive(Clone)]
pub enum BlockLocation {
/// It's part of the canon chain.
CanonChain,
@ -42,6 +44,8 @@ pub enum BlockLocation {
/// Hash of the newest common ancestor with old canon chain.
ancestor: H256,
/// 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>,
}
}

View File

@ -28,7 +28,7 @@ use blockchain::best_block::BestBlock;
use blockchain::bloom_indexer::BloomIndexer;
use blockchain::tree_route::TreeRoute;
use blockchain::update::ExtrasUpdate;
use blockchain::CacheSize;
use blockchain::{CacheSize, ImportRoute};
const BLOOM_INDEX_SIZE: usize = 16;
const BLOOM_LEVELS: u8 = 3;
@ -414,14 +414,14 @@ impl BlockChain {
/// Inserts the block into backing cache database.
/// Expects the block to be valid and already verified.
/// 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
let block = BlockView::new(bytes);
let header = block.header_view();
let hash = header.sha3();
if self.is_known(&hash) {
return;
return ImportRoute::none();
}
// store block in db
@ -435,8 +435,10 @@ impl BlockChain {
block_receipts: self.prepare_block_receipts_update(receipts, &info),
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
info: info
info: info.clone(),
});
ImportRoute::from(info)
}
/// Applies extras update.
@ -549,9 +551,14 @@ impl BlockChain {
match route.blocks.len() {
0 => BlockLocation::CanonChain,
_ => BlockLocation::BranchBecomingCanonChain {
ancestor: route.ancestor,
route: route.blocks.into_iter().skip(route.index).collect()
_ => {
let retracted = route.blocks.iter().take(route.index).cloned().collect::<Vec<H256>>();
BlockLocation::BranchBecomingCanonChain {
ancestor: route.ancestor,
enacted: route.blocks.into_iter().skip(route.index).collect(),
retracted: retracted.into_iter().rev().collect(),
}
}
}
} else {
@ -572,11 +579,11 @@ impl BlockChain {
BlockLocation::CanonChain => {
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 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);
}
@ -661,11 +668,11 @@ impl BlockChain {
ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
.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 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(|bytes| BlockView::new(&bytes).header_view().log_bloom())
.collect();
@ -825,7 +832,7 @@ mod tests {
use rustc_serialize::hex::FromHex;
use util::hash::*;
use util::sha3::Hashable;
use blockchain::{BlockProvider, BlockChain, BlockChainConfig};
use blockchain::{BlockProvider, BlockChain, BlockChainConfig, ImportRoute};
use tests::helpers::*;
use devtools::*;
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
@ -884,7 +891,7 @@ mod tests {
}
#[test]
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
fn test_find_uncles() {
let mut canon_chain = ChainGenerator::default();
let mut finalizer = BlockFinalizer::default();
@ -922,7 +929,7 @@ mod tests {
}
#[test]
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
fn test_small_fork() {
let mut canon_chain = ChainGenerator::default();
let mut finalizer = BlockFinalizer::default();
@ -943,10 +950,30 @@ mod tests {
let temp = RandomTempPath::new();
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
bc.insert_block(&b1, vec![]);
bc.insert_block(&b2, vec![]);
bc.insert_block(&b3a, vec![]);
bc.insert_block(&b3b, vec![]);
let ir1 = bc.insert_block(&b1, vec![]);
let ir2 = bc.insert_block(&b2, vec![]);
let ir3b = 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.block_number(&genesis_hash).unwrap(), 0);

View File

@ -29,7 +29,7 @@ pub trait ChainIterator: Iterator + Sized {
/// Blocks generated by fork will have lower difficulty than current chain.
fn fork(&self, fork_number: usize) -> Fork<Self> where Self: Clone;
/// 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.
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>;
/// 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 {
iter: self,
bloom: bloom

View 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))],
});
}
}

View File

@ -25,7 +25,9 @@ mod tree_route;
mod update;
#[cfg(test)]
mod generator;
mod import_route;
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig};
pub use self::cache::CacheSize;
pub use self::tree_route::TreeRoute;
pub use self::import_route::ImportRoute;

View File

@ -141,6 +141,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get block total difficulty.
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;
/// Get address nonce.
fn nonce(&self, address: &Address) -> U256;
/// Get block hash.
fn block_hash(&self, id: BlockId) -> Option<H256>;
@ -182,6 +185,13 @@ pub trait BlockChainClient : Sync + Send {
/// Returns logs matching given filter.
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)]
@ -370,18 +380,14 @@ impl<V> Client<V> where V: Verifier {
bad_blocks.insert(header.hash());
continue;
}
let closed_block = self.check_and_close_block(&block);
if let Err(_) = closed_block {
bad_blocks.insert(header.hash());
break;
}
// Insert block
let closed_block = closed_block.unwrap();
self.chain.write().unwrap().insert_block(&block.bytes, closed_block.block().receipts().clone());
good_blocks.push(header.hash());
// Are we committing an era?
let ancient = if header.number() >= HISTORY {
let n = header.number() - HISTORY;
let chain = self.chain.read().unwrap();
@ -391,10 +397,17 @@ impl<V> Client<V> where V: Verifier {
};
// Commit results
let closed_block = closed_block.unwrap();
let receipts = closed_block.block().receipts().clone();
closed_block.drain()
.commit(header.number(), &header.hash(), ancient)
.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.write().unwrap()
.insert_block(&block.bytes, receipts);
self.report.write().unwrap().accrue_block(&block);
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
}
@ -404,8 +417,12 @@ impl<V> Client<V> where V: Verifier {
{
let mut block_queue = self.block_queue.write().unwrap();
block_queue.mark_as_bad(&bad_blocks);
block_queue.mark_as_good(&good_blocks);
if !bad_blocks.is_empty() {
block_queue.mark_as_bad(&bad_blocks);
}
if !good_blocks.is_empty() {
block_queue.mark_as_good(&good_blocks);
}
}
{
@ -414,6 +431,8 @@ impl<V> Client<V> where V: Verifier {
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
good: good_blocks,
bad: bad_blocks,
// TODO [todr] were to take those from?
retracted: vec![],
})).unwrap();
}
}
@ -511,39 +530,6 @@ impl<V> Client<V> where V: Verifier {
trace!("Sealing: number={}, hash={}, diff={}", b.hash(), b.block().header().difficulty(), b.block().header().number());
*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
@ -588,6 +574,10 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
Self::block_hash(&chain, id).and_then(|hash| 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> {
let chain = self.chain.read().unwrap();
Self::block_hash(&chain, id)
@ -702,6 +692,39 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
})
.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 {

View File

@ -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 {
fn calculate_difficuty(&self, header: &Header, parent: &Header) -> U256 {
const EXP_DIFF_PERIOD: u64 = 100000;

View File

@ -243,7 +243,7 @@ struct CodeReader<'a> {
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> {
/// Get `no_of_bytes` from code and convert to U256. Move PC
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 {
Gas(U256),
GasMem(U256, U256),
@ -347,7 +347,7 @@ impl evm::Evm for 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,
ext: &evm::Ext,
instruction: Instruction,

View File

@ -25,9 +25,8 @@ struct FakeLogEntry {
}
#[derive(PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature="dev", allow(enum_variant_names))] // Common prefix is C ;)
enum FakeCallType {
CALL, CREATE
Call, Create
}
#[derive(PartialEq, Eq, Hash, Debug)]
@ -94,7 +93,7 @@ impl Ext for FakeExt {
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult {
self.calls.insert(FakeCall {
call_type: FakeCallType::CREATE,
call_type: FakeCallType::Create,
gas: *gas,
sender_address: None,
receive_address: None,
@ -115,7 +114,7 @@ impl Ext for FakeExt {
_output: &mut [u8]) -> MessageCallResult {
self.calls.insert(FakeCall {
call_type: FakeCallType::CALL,
call_type: FakeCallType::Call,
gas: *gas,
sender_address: Some(sender_address.clone()),
receive_address: Some(receive_address.clone()),
@ -909,7 +908,7 @@ fn test_calls(factory: super::Factory) {
};
assert_set_contains(&ext.calls, &FakeCall {
call_type: FakeCallType::CALL,
call_type: FakeCallType::Call,
gas: U256::from(2556),
sender_address: Some(address.clone()),
receive_address: Some(code_address.clone()),
@ -918,7 +917,7 @@ fn test_calls(factory: super::Factory) {
code_address: Some(code_address.clone())
});
assert_set_contains(&ext.calls, &FakeCall {
call_type: FakeCallType::CALL,
call_type: FakeCallType::Call,
gas: U256::from(2556),
sender_address: Some(address.clone()),
receive_address: Some(address.clone()),

View File

@ -188,7 +188,7 @@ impl<'a> Ext for Externalities<'a> {
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> {
match &mut self.output {
&mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe {
@ -226,9 +226,9 @@ impl<'a> Ext for Externalities<'a> {
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
let address = self.origin_info.address.clone();
self.substate.logs.push(LogEntry {
self.substate.logs.push(LogEntry {
address: address,
topics: topics,
topics: topics,
data: data.to_vec()
});
}

View File

@ -15,16 +15,16 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
#![warn(missing_docs)]
#![cfg_attr(feature="dev", feature(plugin))]
#![cfg_attr(feature="dev", plugin(clippy))]
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
// Clippy config
// 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
#![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.
#![cfg_attr(feature="dev", allow(clone_on_copy))]
#![cfg_attr(all(nightly, feature="dev"), allow(clone_on_copy))]
//! Ethcore library
//!

View File

@ -31,6 +31,8 @@ pub enum SyncMessage {
good: Vec<H256>,
/// Hashes of blocks not imported to blockchain
bad: Vec<H256>,
/// Hashes of blocks that were removed from canonical chain
retracted: Vec<H256>,
},
/// A block is ready
BlockVerified,
@ -115,12 +117,11 @@ impl IoHandler<NetSyncMessage> for ClientIoHandler {
}
}
#[cfg_attr(feature="dev", allow(match_ref_pats))]
#[cfg_attr(feature="dev", allow(single_match))]
#[cfg_attr(all(nightly, feature="dev"), allow(single_match))]
fn message(&self, io: &IoContext<NetSyncMessage>, net_message: &NetSyncMessage) {
if let &UserMessage(ref message) = net_message {
match message {
&SyncMessage::BlockVerified => {
if let UserMessage(ref message) = *net_message {
match *message {
SyncMessage::BlockVerified => {
self.client.import_verified_blocks(&io.channel());
},
_ => {}, // ignore other messages

View File

@ -99,7 +99,7 @@ pub struct Spec {
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 {
/// Convert this object into a boxed Engine of the right underlying type.
// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
@ -136,7 +136,7 @@ impl Spec {
uncles_hash: RlpStream::new_list(0).out().sha3(),
extra_data: self.extra_data.clone(),
state_root: self.state_root().clone(),
receipts_root: self.receipts_root.clone(),
receipts_root: self.receipts_root.clone(),
log_bloom: H2048::new().clone(),
gas_used: self.gas_used.clone(),
gas_limit: self.gas_limit.clone(),
@ -182,7 +182,7 @@ impl Spec {
)
}
};
self.parent_hash = H256::from_json(&genesis["parentHash"]);
self.transactions_root = genesis.find("transactionsTrie").and_then(|_| Some(H256::from_json(&genesis["transactionsTrie"]))).unwrap_or(SHA3_NULL_RLP.clone());
self.receipts_root = genesis.find("receiptTrie").and_then(|_| Some(H256::from_json(&genesis["receiptTrie"]))).unwrap_or(SHA3_NULL_RLP.clone());
@ -249,7 +249,7 @@ impl FromJson for Spec {
)
}
};
Spec {
name: json.find("name").map_or("unknown", |j| j.as_string().unwrap()).to_owned(),
engine_name: json["engineName"].as_string().unwrap().to_owned(),
@ -278,7 +278,7 @@ impl Spec {
/// Ensure that the given state DB has the trie nodes in for the genesis state.
pub fn ensure_db_good(&self, db: &mut HashDB) -> bool {
if !db.contains(&self.state_root()) {
let mut root = H256::new();
let mut root = H256::new();
{
let mut t = SecTrieDBMut::new(db, &mut root);
for (address, account) in self.genesis_state.get().iter() {

View File

@ -224,7 +224,7 @@ impl State {
/// 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.
#[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>>) {
// first, commit the sub trees.
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?

View File

@ -80,7 +80,7 @@ impl Transaction {
}
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 {
let t = Transaction {
nonce: xjson!(&json["nonce"]),

View File

@ -17,9 +17,11 @@
pub mod verification;
pub mod verifier;
mod canon_verifier;
#[cfg(test)]
mod noop_verifier;
pub use self::verification::*;
pub use self::verifier::Verifier;
pub use self::canon_verifier::CanonVerifier;
#[cfg(test)]
pub use self::noop_verifier::NoopVerifier;

View File

@ -1,3 +1,3 @@
#!/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

View File

@ -17,8 +17,8 @@
//! Ethcore client application.
#![warn(missing_docs)]
#![cfg_attr(feature="dev", feature(plugin))]
#![cfg_attr(feature="dev", plugin(clippy))]
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
extern crate docopt;
extern crate rustc_serialize;
extern crate ethcore_util as util;
@ -32,6 +32,7 @@ extern crate fdlimit;
extern crate daemonize;
extern crate time;
extern crate number_prefix;
extern crate rpassword;
#[cfg(feature = "rpc")]
extern crate ethcore_rpc as rpc;
@ -43,12 +44,12 @@ use std::path::PathBuf;
use env_logger::LogBuilder;
use ctrlc::CtrlC;
use util::*;
use util::panics::MayPanic;
use util::panics::{MayPanic, ForwardPanic, PanicHandler};
use ethcore::spec::*;
use ethcore::client::*;
use ethcore::service::{ClientService, NetSyncMessage};
use ethcore::ethereum;
use ethsync::{EthSync, SyncConfig};
use ethsync::{EthSync, SyncConfig, SyncProvider};
use docopt::Docopt;
use daemonize::Daemonize;
use number_prefix::{binary_prefix, Standalone, Prefixed};
@ -70,6 +71,7 @@ Parity. Ethereum Client.
Usage:
parity daemon <pid-file> [options] [ --no-bootstrap | <enode>... ]
parity account (new | list)
parity [options] [ --no-bootstrap | <enode>... ]
Protocol Options:
@ -77,7 +79,7 @@ Protocol Options:
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.
--pruning Enable state/storage trie pruning.
--pruning Client should prune the state/storage trie.
-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]
--identity NAME Specify your node's name.
@ -126,6 +128,9 @@ Miscellaneous Options:
#[derive(Debug, RustcDecodable)]
struct Args {
cmd_daemon: bool,
cmd_account: bool,
cmd_new: bool,
cmd_list: bool,
arg_pid_file: String,
arg_enode: Vec<String>,
flag_chain: String,
@ -190,10 +195,10 @@ fn setup_log(init: &Option<String>) {
}
#[cfg(feature = "rpc")]
fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, url: &str, cors_domain: &str, apis: Vec<&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::*;
let mut server = rpc::HttpServer::new(1);
let server = rpc::RpcServer::new();
for api in apis.into_iter() {
match api {
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
@ -207,11 +212,12 @@ fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, url: &str, cors_dom
}
}
}
server.start_async(url, cors_domain);
Some(server.start_http(url, cors_domain, 1))
}
#[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() {
@ -287,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>) {
let mut listen_address = None;
let mut public_address = None;
@ -336,10 +342,44 @@ impl Configuration {
.start()
.unwrap_or_else(|e| die!("Couldn't daemonize; {}", e));
}
if self.args.cmd_account {
self.execute_account_cli();
return;
}
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) {
// Setup panic handler
let panic_handler = PanicHandler::new_in_arc();
// Setup logging
setup_log(&self.args.flag_logging);
// Raise fdlimit
@ -366,6 +406,7 @@ impl Configuration {
client_config.name = self.args.flag_identity.clone();
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();
panic_handler.forward_from(&service);
let client = service.client().clone();
client.set_author(self.author());
client.set_extra_data(self.extra_data());
@ -383,30 +424,36 @@ impl Configuration {
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);
setup_rpc_server(service.client(), sync.clone(), &url, cors, apis.split(",").collect());
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
let io_handler = Arc::new(ClientIoHandler {
client: service.client(),
info: Default::default(),
sync: sync
sync: sync.clone(),
});
service.io().register_handler(io_handler).expect("Error registering IO handler");
// 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());
// Handle possible exits
let e = exit.clone();
CtrlC::set_handler(move || { e.notify_all(); });
// Handle panics
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
let mutex = Mutex::new(());

View File

@ -12,8 +12,8 @@ build = "build.rs"
log = "0.3"
serde = "0.7.0"
serde_json = "0.7.0"
jsonrpc-core = "1.2"
jsonrpc-http-server = "2.1"
jsonrpc-core = "2.0"
jsonrpc-http-server = "3.0"
ethcore-util = { path = "../util" }
ethcore = { path = "../ethcore" }
ethash = { path = "../ethash" }
@ -26,8 +26,9 @@ serde_macros = { version = "0.7.0", optional = true }
[build-dependencies]
serde_codegen = { version = "0.7.0", optional = true }
syntex = "0.29.0"
rustc_version = "0.1"
[features]
default = ["serde_codegen"]
nightly = ["serde_macros"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev"]
dev = ["ethcore/dev", "ethcore-util/dev", "ethsync/dev"]

View File

@ -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"))]
mod inner {
extern crate syntex;
@ -26,4 +46,7 @@ mod inner {
fn main() {
inner::main();
if let Channel::Nightly = version_meta().channel {
println!("cargo:rustc-cfg=nightly");
}
}

View File

@ -29,33 +29,43 @@ extern crate ethcore;
extern crate ethsync;
extern crate transient_hashmap;
use std::sync::Arc;
use std::thread;
use util::panics::PanicHandler;
use self::jsonrpc_core::{IoHandler, IoDelegate};
pub mod v1;
/// Http server.
pub struct HttpServer {
handler: IoHandler,
threads: usize
pub struct RpcServer {
handler: Arc<IoHandler>,
}
impl HttpServer {
impl RpcServer {
/// Construct new http server object with given number of threads.
pub fn new(threads: usize) -> HttpServer {
HttpServer {
handler: IoHandler::new(),
threads: threads
pub fn new() -> RpcServer {
RpcServer {
handler: Arc::new(IoHandler::new()),
}
}
/// 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);
}
/// Start server asynchronously in new thread
pub fn start_async(self, addr: &str, cors_domain: &str) {
let server = jsonrpc_http_server::Server::new(self.handler, self.threads);
server.start_async(addr, jsonrpc_http_server::AccessControlAllowOrigin::Value(cors_domain.to_owned()))
/// Start server asynchronously in new thread and returns panic handler.
pub fn start_http(&self, addr: &str, cors_domain: &str, threads: usize) -> Arc<PanicHandler> {
let addr = addr.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
}
}

View File

@ -84,7 +84,7 @@ impl<F, T> PollManager<F, T> where T: Timer {
}
/// 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.get(id)
}
@ -124,21 +124,21 @@ mod tests {
*time.borrow_mut() = 10;
indexer.update_poll(&0, 21);
assert_eq!(indexer.get_poll_info(&0).unwrap().filter, false);
assert_eq!(indexer.get_poll_info(&0).unwrap().block_number, 21);
assert_eq!(indexer.poll_info(&0).unwrap().filter, false);
assert_eq!(indexer.poll_info(&0).unwrap().block_number, 21);
*time.borrow_mut() = 30;
indexer.update_poll(&1, 23);
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);
assert_eq!(indexer.poll_info(&1).unwrap().filter, true);
assert_eq!(indexer.poll_info(&1).unwrap().block_number, 23);
*time.borrow_mut() = 75;
indexer.update_poll(&0, 30);
assert!(indexer.get_poll_info(&0).is_none());
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);
assert!(indexer.poll_info(&0).is_none());
assert_eq!(indexer.poll_info(&1).unwrap().filter, true);
assert_eq!(indexer.poll_info(&1).unwrap().block_number, 23);
indexer.remove_poll(&1);
assert!(indexer.get_poll_info(&1).is_none());
assert!(indexer.poll_info(&1).is_none());
}
}

View File

@ -17,7 +17,7 @@
//! Eth rpc implementation.
use std::collections::HashMap;
use std::sync::{Arc, Weak, Mutex, RwLock};
use ethsync::{EthSync, SyncState};
use ethsync::{SyncProvider, SyncState};
use jsonrpc_core::*;
use util::numbers::*;
use util::sha3::*;
@ -25,7 +25,6 @@ use util::rlp::encode;
use ethcore::client::*;
use ethcore::block::{IsBlock};
use ethcore::views::*;
//#[macro_use] extern crate log;
use ethcore::ethereum::Ethash;
use ethcore::ethereum::denominations::shannon;
use v1::traits::{Eth, EthFilter};
@ -33,15 +32,15 @@ use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncIn
use v1::helpers::{PollFilter, PollManager};
/// Eth rpc implementation.
pub struct EthClient {
client: Weak<Client>,
sync: Weak<EthSync>,
pub struct EthClient<C, S> where C: BlockChainClient, S: SyncProvider {
client: Weak<C>,
sync: Weak<S>,
hashrates: RwLock<HashMap<H256, u64>>,
}
impl EthClient {
impl<C, S> EthClient<C, S> where C: BlockChainClient, S: SyncProvider {
/// Creates new EthClient.
pub fn new(client: &Arc<Client>, sync: &Arc<EthSync>) -> Self {
pub fn new(client: &Arc<C>, sync: &Arc<S>) -> Self {
EthClient {
client: Arc::downgrade(client),
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: SyncProvider + 'static {
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
match params {
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.
pub struct EthFilterClient {
client: Weak<Client>,
pub struct EthFilterClient<C> where C: BlockChainClient {
client: Weak<C>,
polls: Mutex<PollManager<PollFilter>>,
}
impl EthFilterClient {
impl<C> EthFilterClient<C> where C: BlockChainClient {
/// Creates new Eth filter client.
pub fn new(client: &Arc<Client>) -> Self {
pub fn new(client: &Arc<C>) -> Self {
EthFilterClient {
client: Arc::downgrade(client),
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> {
from_params::<(Filter,)>(params)
.and_then(|(filter,)| {
@ -307,7 +306,7 @@ impl EthFilter for EthFilterClient {
let client = take_weak!(self.client);
from_params::<(Index,)>(params)
.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 {
None => Ok(Value::Array(vec![] as Vec<Value>)),
Some(info) => match info.filter {

View File

@ -17,24 +17,24 @@
//! Net rpc implementation.
use std::sync::{Arc, Weak};
use jsonrpc_core::*;
use ethsync::EthSync;
use ethsync::SyncProvider;
use v1::traits::Net;
/// Net rpc implementation.
pub struct NetClient {
sync: Weak<EthSync>
pub struct NetClient<S> where S: SyncProvider {
sync: Weak<S>
}
impl NetClient {
impl<S> NetClient<S> where S: SyncProvider {
/// Creates new NetClient.
pub fn new(sync: &Arc<EthSync>) -> Self {
pub fn new(sync: &Arc<S>) -> Self {
NetClient {
sync: Arc::downgrade(sync)
}
}
}
impl Net for NetClient {
impl<S> Net for NetClient<S> where S: SyncProvider + 'static {
fn version(&self, _: Params) -> Result<Value, Error> {
Ok(Value::U64(take_weak!(self.sync).status().protocol_version as u64))
}

View File

@ -4,9 +4,13 @@ name = "ethsync"
version = "0.9.99"
license = "GPL-3.0"
authors = ["Ethcore <admin@ethcore.io"]
build = "build.rs"
[lib]
[build-dependencies]
rustc_version = "0.1"
[dependencies]
ethcore-util = { path = "../util" }
ethcore = { path = "../ethcore" }
@ -17,7 +21,8 @@ time = "0.1.34"
rand = "0.3.13"
heapsize = "0.3"
rustc-serialize = "0.3"
rayon = "0.3.1"
[features]
default = []
dev = ["clippy", "ethcore/dev", "ethcore-util/dev"]
dev = ["ethcore/dev", "ethcore-util/dev"]

25
sync/build.rs Normal file
View 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");
}
}

View File

@ -30,16 +30,20 @@
///
use util::*;
use rayon::prelude::*;
use std::mem::{replace};
use ethcore::views::{HeaderView};
use ethcore::views::{HeaderView, BlockView};
use ethcore::header::{BlockNumber, Header as BlockHeader};
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo};
use range_collection::{RangeCollection, ToUsize, FromUsize};
use ethcore::error::*;
use ethcore::block::Block;
use ethcore::transaction::SignedTransaction;
use io::SyncIo;
use transaction_queue::TransactionQueue;
use time;
use super::SyncConfig;
use ethcore;
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
have_common_block: bool,
/// Last propagated block number
last_send_block_number: BlockNumber,
last_sent_block_number: BlockNumber,
/// Max blocks to download ahead
max_download_ahead_blocks: usize,
/// Network ID
network_id: U256,
/// Transactions Queue
transaction_queue: Mutex<TransactionQueue>,
}
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
@ -231,9 +237,10 @@ impl ChainSync {
last_imported_hash: None,
syncing_difficulty: U256::from(0u64),
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),
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
fn reset(&mut self) {
self.downloading_headers.clear();
@ -292,6 +299,7 @@ impl ChainSync {
self.starting_block = 0;
self.highest_block = None;
self.have_common_block = false;
self.transaction_queue.lock().unwrap().clear();
self.starting_block = io.chain().chain_info().best_block_number;
self.state = SyncState::NotSynced;
}
@ -335,7 +343,7 @@ impl ChainSync {
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
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);
@ -462,6 +470,7 @@ impl ChainSync {
}
/// 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> {
let block_rlp = try!(r.at(0));
let header_rlp = try!(block_rlp.at(0));
@ -484,7 +493,7 @@ impl ChainSync {
trace!(target: "sync", "New block already queued {:?}", h);
},
Ok(_) => {
if self.current_base_block() < header.number {
if self.current_base_block() < header.number {
self.last_imported_block = Some(header.number);
self.remove_downloaded_blocks(header.number);
}
@ -843,8 +852,8 @@ impl ChainSync {
self.downloading_bodies.remove(&n);
self.downloading_headers.remove(&n);
}
self.headers.remove_tail(&start);
self.bodies.remove_tail(&start);
self.headers.remove_from(&start);
self.bodies.remove_from(&start);
}
/// Request headers from a peer by block hash
@ -919,8 +928,18 @@ impl ChainSync {
}
}
/// Called when peer sends us new transactions
fn on_peer_transactions(&mut self, _io: &mut SyncIo, _peer_id: PeerId, _r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
Ok(())
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(())
}
/// Send Status message
@ -1229,22 +1248,66 @@ impl ChainSync {
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
pub fn maintain_sync(&mut self, io: &mut SyncIo) {
self.check_resume(io);
}
/// should be called once chain has new block, triggers the latest block propagation
pub fn chain_blocks_verified(&mut self, io: &mut SyncIo) {
let chain = io.chain().chain_info();
if (((chain.best_block_number as i64) - (self.last_send_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION {
let blocks = self.propagate_blocks(&chain, io);
let hashes = self.propagate_new_hashes(&chain, io);
if blocks != 0 || hashes != 0 {
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
}
/// called when block is imported to chain, updates transactions queue and propagates the blocks
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, good: &[H256], bad: &[H256], _retracted: &[H256]) {
fn fetch_transactions(chain: &BlockChainClient, hash: &H256) -> Vec<SignedTransaction> {
let block = chain
.block(BlockId::Hash(hash.clone()))
// Client should send message after commit to db and inserting to chain.
.expect("Expected in-chain blocks.");
let block = BlockView::new(&block);
block.transactions()
}
self.last_send_block_number = chain.best_block_number;
{
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();
}
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);
}
}
@ -1386,7 +1449,7 @@ mod tests {
#[test]
fn finds_lagging_peers() {
let mut client = TestBlockChainClient::new();
client.add_blocks(100, false);
client.add_blocks(100, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(10));
let chain_info = client.chain_info();
@ -1400,7 +1463,7 @@ mod tests {
#[test]
fn calculates_tree_for_lagging_peer() {
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 end = client.block_hash_delta_minus(2);
@ -1417,7 +1480,7 @@ mod tests {
#[test]
fn sends_new_hashes_to_lagging_peer() {
let mut client = TestBlockChainClient::new();
client.add_blocks(100, false);
client.add_blocks(100, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
let chain_info = client.chain_info();
@ -1436,7 +1499,7 @@ mod tests {
#[test]
fn sends_latest_block_to_lagging_peer() {
let mut client = TestBlockChainClient::new();
client.add_blocks(100, false);
client.add_blocks(100, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
let chain_info = client.chain_info();
@ -1454,7 +1517,7 @@ mod tests {
#[test]
fn handles_peer_new_block_mallformed() {
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);
@ -1472,7 +1535,7 @@ mod tests {
#[test]
fn handles_peer_new_block() {
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);
@ -1490,7 +1553,7 @@ mod tests {
#[test]
fn handles_peer_new_block_empty() {
let mut client = TestBlockChainClient::new();
client.add_blocks(10, false);
client.add_blocks(10, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
let mut io = TestIo::new(&mut client, &mut queue, None);
@ -1506,7 +1569,7 @@ mod tests {
#[test]
fn handles_peer_new_hashes() {
let mut client = TestBlockChainClient::new();
client.add_blocks(10, false);
client.add_blocks(10, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
let mut io = TestIo::new(&mut client, &mut queue, None);
@ -1522,7 +1585,7 @@ mod tests {
#[test]
fn handles_peer_new_hashes_empty() {
let mut client = TestBlockChainClient::new();
client.add_blocks(10, false);
client.add_blocks(10, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
let mut io = TestIo::new(&mut client, &mut queue, None);
@ -1540,7 +1603,7 @@ mod tests {
#[test]
fn hashes_rlp_mutually_acceptable() {
let mut client = TestBlockChainClient::new();
client.add_blocks(100, false);
client.add_blocks(100, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
let chain_info = client.chain_info();
@ -1558,7 +1621,7 @@ mod tests {
#[test]
fn block_rlp_mutually_acceptable() {
let mut client = TestBlockChainClient::new();
client.add_blocks(100, false);
client.add_blocks(100, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5));
let chain_info = client.chain_info();
@ -1571,10 +1634,37 @@ mod tests {
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]
fn returns_requested_block_headers() {
let mut client = TestBlockChainClient::new();
client.add_blocks(100, false);
client.add_blocks(100, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let io = TestIo::new(&mut client, &mut queue, None);
@ -1598,7 +1688,7 @@ mod tests {
#[test]
fn returns_requested_block_headers_reverse() {
let mut client = TestBlockChainClient::new();
client.add_blocks(100, false);
client.add_blocks(100, EachBlockWith::Uncle);
let mut queue = VecDeque::new();
let io = TestIo::new(&mut client, &mut queue, None);

View File

@ -15,11 +15,11 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
#![warn(missing_docs)]
#![cfg_attr(feature="dev", feature(plugin))]
#![cfg_attr(feature="dev", plugin(clippy))]
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
// 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
//! Implements ethereum protocol version 63 as specified here:
@ -54,6 +54,7 @@ extern crate ethcore;
extern crate env_logger;
extern crate time;
extern crate rand;
extern crate rayon;
#[macro_use]
extern crate heapsize;
@ -70,8 +71,8 @@ use io::NetSyncIo;
mod chain;
mod io;
mod range_collection;
// TODO [todr] Made public to suppress dead code warnings
pub mod transaction_queue;
mod transaction_queue;
pub use transaction_queue::TransactionQueue;
#[cfg(test)]
mod tests;
@ -93,6 +94,14 @@ impl Default for SyncConfig {
}
}
/// Current sync status
pub trait SyncProvider: Send + Sync {
/// Get sync status
fn status(&self) -> SyncStatus;
/// Insert transaction in the sync transaction queue
fn insert_transaction(&self, transaction: ethcore::transaction::SignedTransaction);
}
/// Ethereum network protocol handler
pub struct EthSync {
/// Shared blockchain client. TODO: this should evetually become an IPC endpoint
@ -114,11 +123,6 @@ impl EthSync {
sync
}
/// Get sync status
pub fn status(&self) -> SyncStatus {
self.sync.read().unwrap().status()
}
/// Stop sync
pub fn stop(&mut self, io: &mut NetworkContext<SyncMessage>) {
self.sync.write().unwrap().abort(&mut NetSyncIo::new(io, self.chain.deref()));
@ -130,6 +134,22 @@ impl EthSync {
}
}
impl SyncProvider for EthSync {
/// Get sync status
fn status(&self) -> SyncStatus {
self.sync.read().unwrap().status()
}
/// Insert transaction in transaction queue
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 NetworkProtocolHandler<SyncMessage> for EthSync {
fn initialize(&self, io: &NetworkContext<SyncMessage>) {
io.register_timer(0, 1000).expect("Error registering sync timer");
@ -153,8 +173,12 @@ impl NetworkProtocolHandler<SyncMessage> for EthSync {
}
fn message(&self, io: &NetworkContext<SyncMessage>, message: &SyncMessage) {
if let SyncMessage::BlockVerified = *message {
self.sync.write().unwrap().chain_blocks_verified(&mut NetSyncIo::new(io, self.chain.deref()));
match *message {
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 */},
}
}
}

View File

@ -42,6 +42,8 @@ pub trait RangeCollection<K, V> {
fn remove_head(&mut self, start: &K);
/// Remove all elements >= `start` in the range that contains `start`
fn remove_tail(&mut self, start: &K);
/// Remove all elements >= `start`
fn remove_from(&mut self, start: &K);
/// Remove all elements >= `tail`
fn insert_item(&mut self, key: K, value: V);
/// 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
fn remove_head(&mut self, key: &K) {
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]
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
fn test_range() {
use std::cmp::{Ordering};
@ -272,5 +296,17 @@ fn test_range() {
assert_eq!(r.range_iter().cmp(vec![(2, &['b'][..])]), Ordering::Equal);
r.remove_tail(&2);
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);
}

View File

@ -24,8 +24,8 @@ use super::helpers::*;
fn two_peers() {
::env_logger::init().ok();
let mut net = TestNet::new(3);
net.peer_mut(1).chain.add_blocks(1000, false);
net.peer_mut(2).chain.add_blocks(1000, false);
net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync();
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());
@ -35,8 +35,8 @@ fn two_peers() {
fn status_after_sync() {
::env_logger::init().ok();
let mut net = TestNet::new(3);
net.peer_mut(1).chain.add_blocks(1000, false);
net.peer_mut(2).chain.add_blocks(1000, false);
net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync();
let status = net.peer(0).sync.status();
assert_eq!(status.state, SyncState::Idle);
@ -45,8 +45,8 @@ fn status_after_sync() {
#[test]
fn takes_few_steps() {
let mut net = TestNet::new(3);
net.peer_mut(1).chain.add_blocks(100, false);
net.peer_mut(2).chain.add_blocks(100, false);
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Uncle);
net.peer_mut(2).chain.add_blocks(100, EachBlockWith::Uncle);
let total_steps = net.sync();
assert!(total_steps < 7);
}
@ -56,8 +56,9 @@ fn empty_blocks() {
::env_logger::init().ok();
let mut net = TestNet::new(3);
for n in 0..200 {
net.peer_mut(1).chain.add_blocks(5, n % 2 == 0);
net.peer_mut(2).chain.add_blocks(5, n % 2 == 0);
let with = if n % 2 == 0 { EachBlockWith::Nothing } else { EachBlockWith::Uncle };
net.peer_mut(1).chain.add_blocks(5, with.clone());
net.peer_mut(2).chain.add_blocks(5, with);
}
net.sync();
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
@ -68,14 +69,14 @@ fn empty_blocks() {
fn forked() {
::env_logger::init().ok();
let mut net = TestNet::new(3);
net.peer_mut(0).chain.add_blocks(300, false);
net.peer_mut(1).chain.add_blocks(300, false);
net.peer_mut(2).chain.add_blocks(300, false);
net.peer_mut(0).chain.add_blocks(100, true); //fork
net.peer_mut(1).chain.add_blocks(200, false);
net.peer_mut(2).chain.add_blocks(200, false);
net.peer_mut(1).chain.add_blocks(100, false); //fork between 1 and 2
net.peer_mut(2).chain.add_blocks(10, true);
net.peer_mut(0).chain.add_blocks(300, EachBlockWith::Uncle);
net.peer_mut(1).chain.add_blocks(300, EachBlockWith::Uncle);
net.peer_mut(2).chain.add_blocks(300, EachBlockWith::Uncle);
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Nothing); //fork
net.peer_mut(1).chain.add_blocks(200, EachBlockWith::Uncle);
net.peer_mut(2).chain.add_blocks(200, EachBlockWith::Uncle);
net.peer_mut(1).chain.add_blocks(100, EachBlockWith::Uncle); //fork between 1 and 2
net.peer_mut(2).chain.add_blocks(10, EachBlockWith::Nothing);
// peer 1 has the best chain of 601 blocks
let peer1_chain = net.peer(1).chain.numbers.read().unwrap().clone();
net.sync();
@ -87,8 +88,8 @@ fn forked() {
#[test]
fn restart() {
let mut net = TestNet::new(3);
net.peer_mut(1).chain.add_blocks(1000, false);
net.peer_mut(2).chain.add_blocks(1000, false);
net.peer_mut(1).chain.add_blocks(1000, EachBlockWith::Uncle);
net.peer_mut(2).chain.add_blocks(1000, EachBlockWith::Uncle);
net.sync_steps(8);
@ -109,8 +110,8 @@ fn status_empty() {
#[test]
fn status_packet() {
let mut net = TestNet::new(2);
net.peer_mut(0).chain.add_blocks(100, false);
net.peer_mut(1).chain.add_blocks(1, false);
net.peer_mut(0).chain.add_blocks(100, EachBlockWith::Uncle);
net.peer_mut(1).chain.add_blocks(1, EachBlockWith::Uncle);
net.start();
@ -123,13 +124,13 @@ fn status_packet() {
#[test]
fn propagate_hashes() {
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.peer_mut(0).chain.add_blocks(10, false);
net.peer_mut(0).chain.add_blocks(10, EachBlockWith::Uncle);
net.sync();
net.trigger_block_verified(0); //first event just sets the marker
net.trigger_block_verified(0);
net.trigger_chain_new_blocks(0); //first event just sets the marker
net.trigger_chain_new_blocks(0);
// 5 peers to sync
assert_eq!(5, net.peer(0).queue.len());
@ -149,12 +150,12 @@ fn propagate_hashes() {
#[test]
fn propagate_blocks() {
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.peer_mut(0).chain.add_blocks(10, false);
net.trigger_block_verified(0); //first event just sets the marker
net.trigger_block_verified(0);
net.peer_mut(0).chain.add_blocks(10, EachBlockWith::Uncle);
net.trigger_chain_new_blocks(0); //first event just sets the marker
net.trigger_chain_new_blocks(0);
assert!(!net.peer(0).queue.is_empty());
// NEW_BLOCK_PACKET
@ -164,7 +165,7 @@ fn propagate_blocks() {
#[test]
fn restart_on_malformed_block() {
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.sync_steps(10);

View File

@ -22,9 +22,10 @@ use io::SyncIo;
use chain::ChainSync;
use ::SyncConfig;
use ethcore::receipt::Receipt;
use ethcore::transaction::LocalizedTransaction;
use ethcore::transaction::{LocalizedTransaction, Transaction, Action};
use ethcore::filter::Filter;
use ethcore::log_entry::LocalizedLogEntry;
use ethcore::block::ClosedBlock;
pub struct TestBlockChainClient {
pub blocks: RwLock<HashMap<H256, Bytes>>,
@ -34,6 +35,14 @@ pub struct TestBlockChainClient {
pub difficulty: RwLock<U256>,
}
#[derive(Clone)]
pub enum EachBlockWith {
Nothing,
Uncle,
Transaction,
UncleAndTransaction
}
impl TestBlockChainClient {
pub fn new() -> TestBlockChainClient {
@ -44,30 +53,53 @@ impl TestBlockChainClient {
last_hash: RwLock::new(H256::new()),
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
}
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();
for n in len..(len + count) {
let mut header = BlockHeader::new();
header.difficulty = From::from(n);
header.parent_hash = self.last_hash.read().unwrap().clone();
header.number = n as BlockNumber;
let mut uncles = RlpStream::new_list(if empty {0} else {1});
if !empty {
let mut uncle_header = BlockHeader::new();
uncle_header.difficulty = From::from(n);
uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
uncle_header.number = n as BlockNumber;
uncles.append(&uncle_header);
header.uncles_hash = uncles.as_raw().sha3();
}
let uncles = match with {
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
let mut uncles = RlpStream::new_list(1);
let mut uncle_header = BlockHeader::new();
uncle_header.difficulty = From::from(n);
uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
uncle_header.number = n as BlockNumber;
uncles.append(&uncle_header);
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);
rlp.append(&header);
rlp.append_raw(&rlp::NULL_RLP, 1);
rlp.append_raw(&txs, 1);
rlp.append_raw(uncles.as_raw(), 1);
self.import_block(rlp.as_raw().to_vec()).unwrap();
}
@ -109,6 +141,10 @@ impl BlockChainClient for TestBlockChainClient {
unimplemented!();
}
fn nonce(&self, _address: &Address) -> U256 {
U256::zero()
}
fn code(&self, _address: &Address) -> Option<Bytes> {
unimplemented!();
}
@ -125,6 +161,14 @@ impl BlockChainClient for TestBlockChainClient {
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> {
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())
}
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);
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), &[], &[], &[]);
}
}

View File

@ -17,6 +17,67 @@
// TODO [todr] - own transactions should have higher priority
//! 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::collections::{HashMap, BTreeSet};
@ -24,12 +85,20 @@ use util::numbers::{Uint, U256};
use util::hash::{Address, H256};
use util::table::*;
use ethcore::transaction::*;
use ethcore::error::Error;
#[derive(Clone, Debug)]
/// Light structure used to identify transaction and it's order
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,
/// Gas Price of the transaction.
/// Low gas price = Low priority (processed later)
gas_price: U256,
/// Hash to identify associated transaction
hash: H256,
}
@ -70,7 +139,7 @@ impl Ord for TransactionOrder {
let a_gas = self.gas_price;
let b_gas = b.gas_price;
if a_gas != b_gas {
return a_gas.cmp(&b_gas);
return b_gas.cmp(&a_gas);
}
// Compare hashes
@ -78,14 +147,16 @@ impl Ord for TransactionOrder {
}
}
/// Verified transaction (with sender)
struct VerifiedTransaction {
transaction: SignedTransaction
}
impl VerifiedTransaction {
fn new(transaction: SignedTransaction) -> Self {
VerifiedTransaction {
fn new(transaction: SignedTransaction) -> Result<Self, Error> {
try!(transaction.sender());
Ok(VerifiedTransaction {
transaction: transaction
}
})
}
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 {
by_priority: BTreeSet<TransactionOrder>,
by_address: Table<Address, U256, TransactionOrder>,
@ -108,30 +184,37 @@ struct 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_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();
if len <= self.limit {
return;
}
let to_drop : Vec<&VerifiedTransaction> = {
let to_drop : Vec<(Address, U256)> = {
self.by_priority
.iter()
.skip(self.limit)
.map(|order| by_hash.get(&order.hash).expect("Inconsistency in queue detected."))
.map(|tx| (tx.sender(), tx.nonce()))
.collect()
};
for tx in to_drop {
self.drop(&tx.sender(), &tx.nonce());
for (sender, nonce) in to_drop {
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> {
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
self.by_priority.remove(&tx_order);
@ -140,12 +223,15 @@ impl TransactionSet {
None
}
/// Drop all transactions.
fn clear(&mut self) {
self.by_priority.clear();
self.by_address.clear();
}
}
// Will be used when rpc merged
#[allow(dead_code)]
#[derive(Debug)]
/// Current status of the queue
pub struct TransactionQueueStatus {
@ -194,6 +280,8 @@ impl TransactionQueue {
}
}
// Will be used when rpc merged
#[allow(dead_code)]
/// Returns current status for this queue
pub fn status(&self) -> TransactionQueueStatus {
TransactionQueueStatus {
@ -203,60 +291,95 @@ impl TransactionQueue {
}
/// 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 {
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
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 {
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
///
/// 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 {
for tx in txs {
self.remove(&tx, &fetch_nonce);
for hash in transaction_hashes {
self.remove(&hash, &fetch_nonce);
}
}
/// Removes transaction identified by hashes from queue.
///
/// 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 {
let transaction = self.by_hash.remove(hash);
let transaction = self.by_hash.remove(transaction_hash);
if transaction.is_none() {
// We don't know this transaction
return;
}
let transaction = transaction.unwrap();
let sender = transaction.sender();
let nonce = transaction.nonce();
let current_nonce = fetch_nonce(&sender);
println!("Removing tx: {:?}", transaction.transaction);
// Remove from future
self.future.drop(&sender, &nonce);
// Remove from current
let order = self.current.drop(&sender, &nonce);
if order.is_none() {
let order = self.future.drop(&sender, &nonce);
if order.is_some() {
self.update_future(&sender, current_nonce);
// And now lets check if there is some chain of transactions in future
// that should be placed in current
self.move_matching_future_to_current(sender.clone(), current_nonce, current_nonce);
return;
}
// Let's remove transactions where tx.nonce < current_nonce
// and if there are any future transactions matching current_nonce+1 - move to current
let current_nonce = fetch_nonce(&sender);
// We will either move transaction to future or remove it completely
// so there will be no transactions from this sender in current
self.last_nonces.remove(&sender);
// Remove from current
let order = self.current.drop(&sender, &nonce);
if order.is_some() {
// We will either move transaction to future or remove it completely
// so there will be no transactions from this sender in current
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) {
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
None => vec![],
@ -266,22 +389,17 @@ impl TransactionQueue {
// Goes to future or is removed
let order = self.current.drop(&sender, &k).unwrap();
if k >= current_nonce {
println!("Moving to future: {:?}", order);
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
} else {
self.by_hash.remove(&order.hash);
}
}
self.future.enforce_limit(&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);
}
self.future.enforce_limit(&mut self.by_hash);
}
/// 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> {
self.current.by_priority
.iter()
@ -299,67 +417,102 @@ impl TransactionQueue {
self.last_nonces.clear();
}
fn move_future_txs(&mut self, address: Address, current_nonce: U256, first_nonce: U256) -> Option<U256> {
println!("Moving from future for: {:?} base: {:?}", current_nonce, first_nonce);
let mut current_nonce = current_nonce + U256::one();
/// Checks if there are any transactions in `future` that should actually be promoted to `current`
/// (because nonce matches).
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);
if let None = by_nonce {
return None;
return;
}
let mut by_nonce = by_nonce.unwrap();
while let Some(order) = by_nonce.remove(&current_nonce) {
// remove also from priority and hash
self.future.by_priority.remove(&order);
// Put to current
println!("Moved: {:?}", order);
let order = order.update_height(current_nonce.clone(), first_nonce);
self.current.insert(address.clone(), current_nonce, order);
current_nonce = current_nonce + U256::one();
}
}
self.future.by_address.clear_if_empty(&address);
// Returns last inserted nonce
Some(current_nonce - U256::one())
// Update last inserted nonce
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)
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
.get(&address)
.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
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
self.future.insert(address, nonce, order);
self.future.enforce_limit(&self.by_hash);
Self::replace_transaction(tx, next_nonce, &mut self.future, &mut self.by_hash);
self.future.enforce_limit(&mut self.by_hash);
return;
} else if next_nonce > nonce {
} else if nonce < state_nonce {
// Droping transaction
trace!(target: "sync", "Dropping transaction with nonce: {} - expecting: {}", nonce, next_nonce);
return;
}
let base_nonce = fetch_nonce(&address);
let order = TransactionOrder::for_transaction(&tx, base_nonce);
// Insert to by_hash
self.by_hash.insert(tx.hash(), tx);
// Insert to current
self.current.insert(address.clone(), nonce, order);
Self::replace_transaction(tx, base_nonce.clone(), &mut self.current, &mut self.by_hash);
self.last_nonces.insert(address.clone(), nonce);
// But maybe there are some more items waiting in future?
let new_last_nonce = self.move_future_txs(address.clone(), nonce, base_nonce);
self.last_nonces.insert(address.clone(), new_last_nonce.unwrap_or(nonce));
// Enforce limit
self.current.enforce_limit(&self.by_hash);
self.move_matching_future_to_current(address.clone(), nonce + U256::one(), base_nonce);
self.current.enforce_limit(&mut 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)]
mod test {
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::*;
use ethcore::transaction::*;
use super::*;
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
@ -416,12 +565,12 @@ mod test {
limit: 1
};
let (tx1, tx2) = new_txs(U256::from(1));
let tx1 = VerifiedTransaction::new(tx1);
let tx2 = VerifiedTransaction::new(tx2);
let by_hash = {
let tx1 = VerifiedTransaction::new(tx1).unwrap();
let tx2 = VerifiedTransaction::new(tx2).unwrap();
let mut by_hash = {
let mut x = HashMap::new();
let tx1 = VerifiedTransaction::new(tx1.transaction.clone());
let tx2 = VerifiedTransaction::new(tx2.transaction.clone());
let tx1 = VerifiedTransaction::new(tx1.transaction.clone()).unwrap();
let tx2 = VerifiedTransaction::new(tx2.transaction.clone()).unwrap();
x.insert(tx1.hash(), tx1);
x.insert(tx2.hash(), tx2);
x
@ -435,9 +584,10 @@ mod test {
assert_eq!(set.by_address.len(), 2);
// when
set.enforce_limit(&by_hash);
set.enforce_limit(&mut by_hash);
// then
assert_eq!(by_hash.len(), 1);
assert_eq!(set.by_priority.len(), 1);
assert_eq!(set.by_address.len(), 1);
assert_eq!(set.by_priority.iter().next().unwrap().clone(), order1);
@ -454,13 +604,39 @@ mod test {
let tx = new_tx();
// when
txq.add(tx, &default_nonce);
let res = txq.add(tx, &default_nonce);
// then
assert!(res.is_ok());
let stats = txq.status();
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]
fn should_import_txs_from_same_sender() {
// given
@ -469,8 +645,8 @@ mod test {
let (tx, tx2) = new_txs(U256::from(1));
// when
txq.add(tx.clone(), &default_nonce);
txq.add(tx2.clone(), &default_nonce);
txq.add(tx.clone(), &default_nonce).unwrap();
txq.add(tx2.clone(), &default_nonce).unwrap();
// then
let top = txq.top_transactions(5);
@ -487,8 +663,8 @@ mod test {
let (tx, tx2) = new_txs(U256::from(2));
// when
txq.add(tx.clone(), &default_nonce);
txq.add(tx2.clone(), &default_nonce);
txq.add(tx.clone(), &default_nonce).unwrap();
txq.add(tx2.clone(), &default_nonce).unwrap();
// then
let stats = txq.status();
@ -499,6 +675,28 @@ mod test {
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]
fn should_move_transactions_if_gap_filled() {
// given
@ -509,13 +707,13 @@ mod test {
let tx1 = new_unsigned_tx(U256::from(124)).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);
txq.add(tx2, &default_nonce);
txq.add(tx2, &default_nonce).unwrap();
assert_eq!(txq.status().future, 1);
// when
txq.add(tx1, &default_nonce);
txq.add(tx1, &default_nonce).unwrap();
// then
let stats = txq.status();
@ -528,8 +726,8 @@ mod test {
// given
let mut txq2 = TransactionQueue::new();
let (tx, tx2) = new_txs(U256::from(3));
txq2.add(tx.clone(), &default_nonce);
txq2.add(tx2.clone(), &default_nonce);
txq2.add(tx.clone(), &default_nonce).unwrap();
txq2.add(tx2.clone(), &default_nonce).unwrap();
assert_eq!(txq2.status().pending, 1);
assert_eq!(txq2.status().future, 1);
@ -550,10 +748,10 @@ mod test {
let mut txq = TransactionQueue::new();
let (tx, tx2) = new_txs(U256::from(1));
let tx3 = new_tx();
txq.add(tx2.clone(), &default_nonce);
txq.add(tx2.clone(), &default_nonce).unwrap();
assert_eq!(txq.status().future, 1);
txq.add(tx3.clone(), &default_nonce);
txq.add(tx.clone(), &default_nonce);
txq.add(tx3.clone(), &default_nonce).unwrap();
txq.add(tx.clone(), &default_nonce).unwrap();
assert_eq!(txq.status().pending, 3);
// when
@ -572,8 +770,8 @@ mod test {
let (tx, tx2) = new_txs(U256::one());
// add
txq.add(tx2.clone(), &default_nonce);
txq.add(tx.clone(), &default_nonce);
txq.add(tx2.clone(), &default_nonce).unwrap();
txq.add(tx.clone(), &default_nonce).unwrap();
let stats = txq.status();
assert_eq!(stats.pending, 2);
@ -590,11 +788,11 @@ mod test {
// given
let mut txq = TransactionQueue::with_limits(1, 1);
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);
// when
txq.add(tx2.clone(), &default_nonce);
txq.add(tx2.clone(), &default_nonce).unwrap();
// then
let t = txq.top_transactions(2);
@ -608,14 +806,14 @@ mod test {
let mut txq = TransactionQueue::with_limits(10, 1);
let (tx1, tx2) = new_txs(U256::from(4));
let (tx3, tx4) = new_txs(U256::from(4));
txq.add(tx1.clone(), &default_nonce);
txq.add(tx3.clone(), &default_nonce);
txq.add(tx1.clone(), &default_nonce).unwrap();
txq.add(tx3.clone(), &default_nonce).unwrap();
assert_eq!(txq.status().pending, 2);
// when
txq.add(tx2.clone(), &default_nonce);
txq.add(tx2.clone(), &default_nonce).unwrap();
assert_eq!(txq.status().future, 1);
txq.add(tx4.clone(), &default_nonce);
txq.add(tx4.clone(), &default_nonce).unwrap();
// then
assert_eq!(txq.status().future, 1);
@ -629,7 +827,7 @@ mod test {
let fetch_last_nonce = |_a: &Address| last_nonce;
// when
txq.add(tx, &fetch_last_nonce);
txq.add(tx, &fetch_last_nonce).unwrap();
// then
let stats = txq.status();
@ -638,19 +836,38 @@ mod 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
let mut txq = TransactionQueue::new();
let (tx1, tx2) = new_txs(U256::from(1));
txq.add(tx1.clone(), &default_nonce);
txq.add(tx2.clone(), &default_nonce);
txq.add(tx1.clone(), &default_nonce).unwrap();
txq.add(tx2.clone(), &default_nonce).unwrap();
assert_eq!(txq.status().pending, 2);
// when
txq.remove(&tx1.hash(), &default_nonce);
assert_eq!(txq.status().pending, 0);
assert_eq!(txq.status().future, 1);
txq.add(tx1.clone(), &default_nonce);
txq.add(tx1.clone(), &default_nonce).unwrap();
// then
let stats = txq.status();
@ -665,10 +882,10 @@ mod test {
let mut txq = TransactionQueue::new();
let (tx, tx2) = new_txs(U256::from(1));
let tx3 = new_tx();
txq.add(tx2.clone(), &default_nonce);
txq.add(tx2.clone(), &default_nonce).unwrap();
assert_eq!(txq.status().future, 1);
txq.add(tx3.clone(), &default_nonce);
txq.add(tx.clone(), &default_nonce);
txq.add(tx3.clone(), &default_nonce).unwrap();
txq.add(tx.clone(), &default_nonce).unwrap();
assert_eq!(txq.status().pending, 3);
// when
@ -680,4 +897,76 @@ mod test {
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);
}
}

View File

@ -40,7 +40,7 @@ chrono = "0.2"
[features]
default = []
dev = ["clippy"]
dev = []
[build-dependencies]
vergen = "*"

View File

@ -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 {
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)); }
@ -1485,7 +1485,7 @@ mod tests {
}
#[test]
#[cfg_attr(feature="dev", allow(eq_op))]
#[cfg_attr(all(nightly, feature="dev"), allow(eq_op))]
pub fn uint256_comp_test() {
let small = U256([10u64, 0, 0, 0]);
let big = U256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
@ -2032,7 +2032,7 @@ mod tests {
#[test]
#[cfg_attr(feature = "dev", allow(cyclomatic_complexity))]
#[cfg_attr(all(nightly, feature="dev"), allow(cyclomatic_complexity))]
fn u256_multi_full_mul() {
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);

View File

@ -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;
use vergen::*;
use rustc_version::{version_meta, Channel};
fn main() {
vergen(OutputFns::all()).unwrap();
if let Channel::Nightly = version_meta().channel {
println!("cargo:rustc-cfg=nightly");
}
}

View File

@ -305,7 +305,7 @@ macro_rules! impl_hash {
}
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 {
fn clone(&self) -> $from {
unsafe {
@ -637,7 +637,7 @@ mod tests {
use std::str::FromStr;
#[test]
#[cfg_attr(feature="dev", allow(eq_op))]
#[cfg_attr(all(nightly, feature="dev"), allow(eq_op))]
fn hash() {
let h = H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
assert_eq!(H64::from_str("0123456789abcdef").unwrap(), h);

View File

@ -153,7 +153,7 @@ struct UserTimer {
pub struct IoManager<Message> where Message: Send + Sync {
timers: Arc<RwLock<HashMap<HandlerId, UserTimer>>>,
handlers: Vec<Arc<IoHandler<Message>>>,
_workers: Vec<Worker>,
workers: Vec<Worker>,
worker_channel: chase_lev::Worker<Work<Message>>,
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())),
handlers: Vec::new(),
worker_channel: worker,
_workers: workers,
workers: workers,
work_ready: work_ready,
};
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) {
match msg {
IoMessage::Shutdown => event_loop.shutdown(),
IoMessage::Shutdown => {
self.workers.clear();
event_loop.shutdown();
},
IoMessage::AddHandler { handler } => {
let handler_id = {
self.handlers.push(handler.clone());

View File

@ -30,7 +30,7 @@ use std::env;
/// If `journal_overlay` 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
/// immediately. Rather some age (based on a linear but arbitrary metric) must pass before
/// the removals actually take effect.
@ -339,7 +339,7 @@ impl JournalDB {
}
impl HashDB for JournalDB {
fn keys(&self) -> HashMap<H256, i32> {
fn keys(&self) -> HashMap<H256, i32> {
let mut ret: HashMap<H256, i32> = HashMap::new();
for (key, _) in self.backing.iter() {
let h = H256::from_slice(key.deref());
@ -376,7 +376,7 @@ impl HashDB for JournalDB {
}
}
fn exists(&self, key: &H256) -> bool {
fn exists(&self, key: &H256) -> bool {
self.lookup(key).is_some()
}

View File

@ -84,6 +84,7 @@ impl SecretStore {
let mut path = ::std::env::home_dir().expect("Failed to get home dir");
path.push(".parity");
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)
}

View File

@ -55,8 +55,7 @@ pub struct DatabaseIterator<'a> {
impl<'a> Iterator for DatabaseIterator<'a> {
type Item = (Box<[u8]>, Box<[u8]>);
#[cfg_attr(feature="dev", allow(type_complexity))]
fn next(&mut self) -> Option<(Box<[u8]>, Box<[u8]>)> {
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}

View File

@ -15,18 +15,18 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
#![warn(missing_docs)]
#![cfg_attr(feature="dev", feature(plugin))]
#![cfg_attr(feature="dev", plugin(clippy))]
#![cfg_attr(all(nightly, feature="dev"), feature(plugin))]
#![cfg_attr(all(nightly, feature="dev"), plugin(clippy))]
// Clippy settings
// 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
#![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
#![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.
#![cfg_attr(feature="dev", allow(clone_on_copy))]
#![cfg_attr(all(nightly, feature="dev"), allow(clone_on_copy))]
//! Ethcore-util library
//!

View File

@ -113,14 +113,14 @@ impl Discovery {
}
/// Add a new node to discovery table. Pings the node.
pub fn add_node(&mut self, e: NodeEntry) {
pub fn add_node(&mut self, e: NodeEntry) {
let endpoint = e.endpoint.clone();
self.update_node(e);
self.ping(&endpoint);
}
/// Add a list of known nodes to the table.
pub fn init_node_list(&mut self, mut nodes: Vec<NodeEntry>) {
pub fn init_node_list(&mut self, mut nodes: Vec<NodeEntry>) {
for n in nodes.drain(..) {
self.update_node(n);
}
@ -243,7 +243,7 @@ impl Discovery {
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> {
let mut found: BTreeMap<u32, Vec<&NodeEntry>> = BTreeMap::new();
let mut count = 0;
@ -251,7 +251,7 @@ impl Discovery {
// Sort nodes by distance to target
for bucket in buckets {
for node in &bucket.nodes {
let distance = Discovery::distance(target, &node.address.id);
let distance = Discovery::distance(target, &node.address.id);
found.entry(distance).or_insert_with(Vec::new).push(&node.address);
if count == BUCKET_SIZE {
// delete the most distant element
@ -310,7 +310,7 @@ impl Discovery {
None
}),
Ok(_) => None,
Err(e) => {
Err(e) => {
warn!("Error reading UPD socket: {:?}", e);
None
}
@ -339,7 +339,7 @@ impl Discovery {
PACKET_PONG => self.on_pong(&rlp, &node_id, &from),
PACKET_FIND_NODE => self.on_find_node(&rlp, &node_id, &from),
PACKET_NEIGHBOURS => self.on_neighbours(&rlp, &node_id, &from),
_ => {
_ => {
debug!("Unknown UDP packet: {}", packet_id);
Ok(None)
}
@ -367,14 +367,14 @@ impl Discovery {
}
else {
self.update_node(entry.clone());
added_map.insert(node.clone(), entry);
added_map.insert(node.clone(), entry);
}
let hash = rlp.as_raw().sha3();
let mut response = RlpStream::new_list(2);
dest.to_rlp_list(&mut response);
response.append(&hash);
self.send_packet(PACKET_PONG, from, &response.drain());
Ok(Some(TableUpdates { added: added_map, removed: HashSet::new() }))
}
@ -391,7 +391,7 @@ impl Discovery {
}
self.clear_ping(node);
let mut added_map = HashMap::new();
added_map.insert(node.clone(), entry);
added_map.insert(node.clone(), entry);
Ok(None)
}
@ -466,8 +466,8 @@ impl Discovery {
pub fn round(&mut self) -> Option<TableUpdates> {
let removed = self.check_expired(false);
self.discover();
if !removed.is_empty() {
Some(TableUpdates { added: HashMap::new(), removed: removed })
if !removed.is_empty() {
Some(TableUpdates { added: HashMap::new(), removed: removed })
} else { None }
}

View File

@ -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());
}
#[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>>) {
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);
}
#[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>>) {
let nonce = self.info.write().unwrap().next_nonce();
let mut handshakes = self.handshakes.write().unwrap();

View File

@ -71,7 +71,7 @@ impl PanicHandler {
/// Invoke closure and catch any possible panics.
/// 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 {
let _guard = PanicGuard { handler: self };
let result = g();

View File

@ -22,7 +22,7 @@ use super::trietraits::*;
use super::node::*;
/// A `Trie` implementation using a generic `HashDB` backing database.
///
///
/// Use it as a `Trie` trait object. You can use `db()` to get the backing database object, `keys`
/// to get the keys belonging to the trie in the backing database, and `db_items_remaining()` to get
/// which items in the backing database do not belong to this trie. If this is the only trie in the
@ -54,7 +54,7 @@ pub struct TrieDB<'db> {
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> {
/// Create a new trie with the backing database `db` and `root`
/// Panics, if `root` does not exist
@ -63,16 +63,16 @@ impl<'db> TrieDB<'db> {
flushln!("TrieDB::new({}): Trie root not found!", root);
panic!("Trie root not found!");
}
TrieDB {
db: db,
TrieDB {
db: db,
root: root,
hash_count: 0
hash_count: 0
}
}
/// Get the backing database.
pub fn db(&'db self) -> &'db HashDB {
self.db
pub fn db(&'db self) -> &'db HashDB {
self.db
}
/// Determine all the keys in the backing database that belong to the trie.
@ -142,7 +142,7 @@ impl<'db> TrieDB<'db> {
/// Indentation helper for `formal_all`.
fn fmt_indent(&self, f: &mut fmt::Formatter, size: usize) -> fmt::Result {
for _ in 0..size {
for _ in 0..size {
try!(write!(f, " "));
}
Ok(())
@ -358,7 +358,7 @@ impl<'db> fmt::Debug for TrieDB<'db> {
fn iterator() {
use memorydb::*;
use super::triedbmut::*;
let d = vec![ &b"A"[..], &b"AA"[..], &b"AB"[..], &b"B"[..] ];
let mut memdb = MemoryDB::new();

View File

@ -23,7 +23,7 @@ use super::journal::*;
use super::trietraits::*;
/// A `Trie` implementation using a generic `HashDB` backing database.
///
///
/// Use it as a `Trie` trait object. You can use `db()` to get the backing database object, `keys`
/// to get the keys belonging to the trie in the backing database, and `db_items_remaining()` to get
/// which items in the backing database do not belong to this trie. If this is the only trie in the
@ -66,21 +66,21 @@ enum MaybeChanged<'a> {
Changed(Bytes),
}
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
#[cfg_attr(all(nightly, feature="dev"), allow(wrong_self_convention))]
impl<'db> TrieDBMut<'db> {
/// Create a new trie with the backing database `db` and empty `root`
/// Initialise to the state entailed by the genesis block.
/// This guarantees the trie is built correctly.
pub fn new(db: &'db mut HashDB, root: &'db mut H256) -> Self {
pub fn new(db: &'db mut HashDB, root: &'db mut H256) -> Self {
let mut r = TrieDBMut{
db: db,
db: db,
root: root,
hash_count: 0
};
hash_count: 0
};
// set root rlp
*r.root = SHA3_NULL_RLP.clone();
r
*r.root = SHA3_NULL_RLP.clone();
r
}
/// Create a new trie with the backing database `db` and `root`.
@ -91,21 +91,21 @@ impl<'db> TrieDBMut<'db> {
flushln!("Trie root not found {}", root);
panic!("Trie root not found!");
}
TrieDBMut {
db: db,
TrieDBMut {
db: db,
root: root,
hash_count: 0
hash_count: 0
}
}
/// Get the backing database.
pub fn db(&'db self) -> &'db HashDB {
self.db
pub fn db(&'db self) -> &'db HashDB {
self.db
}
/// Get the backing database.
pub fn db_mut(&'db mut self) -> &'db mut HashDB {
self.db
pub fn db_mut(&'db mut self) -> &'db mut HashDB {
self.db
}
/// Determine all the keys in the backing database that belong to the trie.
@ -184,7 +184,7 @@ impl<'db> TrieDBMut<'db> {
/// Indentation helper for `formal_all`.
fn fmt_indent(&self, f: &mut fmt::Formatter, size: usize) -> fmt::Result {
for _ in 0..size {
for _ in 0..size {
try!(write!(f, " "));
}
Ok(())
@ -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
/// 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.
@ -378,7 +378,7 @@ impl<'db> TrieDBMut<'db> {
// original had empty slot - place a leaf there.
true if old_rlp.at(i).is_empty() => journal.new_node(Self::compose_leaf(&partial.mid(1), value), &mut s),
// original has something there already; augment.
true => {
true => {
let new = self.augmented(self.take_node(&old_rlp.at(i), journal), &partial.mid(1), value, journal);
journal.new_node(new, &mut s);
}