Merge branch 'master' of github.com:ethcore/parity into beta
This commit is contained in:
commit
c38d15ad4d
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -261,6 +261,7 @@ dependencies = [
|
||||
"ethjson 0.1.0",
|
||||
"ethstore 0.1.0",
|
||||
"heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -376,6 +377,7 @@ dependencies = [
|
||||
name = "ethcore-util"
|
||||
version = "1.2.1"
|
||||
dependencies = [
|
||||
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bigint 0.1.0",
|
||||
"chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -547,7 +549,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.9.4"
|
||||
source = "git+https://github.com/ethcore/hyper#7ccfcb2aa7e6aa6300efa8cebd6a0e6ce55582ea"
|
||||
source = "git+https://github.com/ethcore/hyper#9e346c1d4bc30cd4142dea9d8a0b117d30858ca4"
|
||||
dependencies = [
|
||||
"cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -556,7 +558,7 @@ dependencies = [
|
||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rotor 0.6.3 (git+https://github.com/ethcore/rotor)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"spmc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1222,7 +1224,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spmc"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -32,6 +32,10 @@ bloomchain = "0.1"
|
||||
rayon = "0.3.1"
|
||||
ethstore = { path = "../ethstore" }
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/ethcore/hyper"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
jit = ["evmjit"]
|
||||
evm-debug = []
|
||||
|
@ -164,6 +164,12 @@ pub trait IsBlock {
|
||||
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
|
||||
}
|
||||
|
||||
/// Trait for a object that has a state database.
|
||||
pub trait Drain {
|
||||
/// Drop this object and return the underlieing database.
|
||||
fn drain(self) -> Box<JournalDB>;
|
||||
}
|
||||
|
||||
impl IsBlock for ExecutedBlock {
|
||||
fn block(&self) -> &ExecutedBlock { self }
|
||||
}
|
||||
@ -436,9 +442,11 @@ impl LockedBlock {
|
||||
_ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drain for LockedBlock {
|
||||
/// Drop this object and return the underlieing database.
|
||||
pub fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
||||
fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
||||
}
|
||||
|
||||
impl SealedBlock {
|
||||
@ -450,9 +458,11 @@ impl SealedBlock {
|
||||
block_rlp.append_raw(&self.uncle_bytes, 1);
|
||||
block_rlp.out()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drain for SealedBlock {
|
||||
/// Drop this object and return the underlieing database.
|
||||
pub fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
||||
fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
||||
}
|
||||
|
||||
impl IsBlock for SealedBlock {
|
||||
|
@ -249,7 +249,7 @@ impl Client {
|
||||
Ok(locked_block)
|
||||
}
|
||||
|
||||
fn calculate_enacted_retracted(&self, import_results: Vec<ImportRoute>) -> (Vec<H256>, Vec<H256>) {
|
||||
fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec<H256>, Vec<H256>) {
|
||||
fn map_to_vec(map: Vec<(H256, bool)>) -> Vec<H256> {
|
||||
map.into_iter().map(|(k, _v)| k).collect()
|
||||
}
|
||||
@ -259,12 +259,12 @@ impl Client {
|
||||
// could be retracted in import `k+1`. This is why to understand if after all inserts
|
||||
// the block is enacted or retracted we iterate over all routes and at the end final state
|
||||
// will be in the hashmap
|
||||
let map = import_results.into_iter().fold(HashMap::new(), |mut map, route| {
|
||||
for hash in route.enacted {
|
||||
map.insert(hash, true);
|
||||
let map = import_results.iter().fold(HashMap::new(), |mut map, route| {
|
||||
for hash in &route.enacted {
|
||||
map.insert(hash.clone(), true);
|
||||
}
|
||||
for hash in route.retracted {
|
||||
map.insert(hash, false);
|
||||
for hash in &route.retracted {
|
||||
map.insert(hash.clone(), false);
|
||||
}
|
||||
map
|
||||
});
|
||||
@ -301,36 +301,10 @@ impl Client {
|
||||
invalid_blocks.insert(header.hash());
|
||||
continue;
|
||||
}
|
||||
let closed_block = closed_block.unwrap();
|
||||
imported_blocks.push(header.hash());
|
||||
|
||||
// Are we committing an era?
|
||||
let ancient = if header.number() >= HISTORY {
|
||||
let n = header.number() - HISTORY;
|
||||
Some((n, self.chain.block_hash(n).unwrap()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Commit results
|
||||
let closed_block = closed_block.unwrap();
|
||||
let receipts = closed_block.block().receipts().clone();
|
||||
let traces = From::from(closed_block.block().traces().clone().unwrap_or_else(Vec::new));
|
||||
|
||||
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)
|
||||
let route = self.chain.insert_block(&block.bytes, receipts);
|
||||
self.tracedb.import(TraceImportRequest {
|
||||
traces: traces,
|
||||
block_hash: header.hash(),
|
||||
block_number: header.number(),
|
||||
enacted: route.enacted.clone(),
|
||||
retracted: route.retracted.len()
|
||||
});
|
||||
|
||||
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
|
||||
import_results.push(route);
|
||||
|
||||
self.report.write().unwrap().accrue_block(&block);
|
||||
@ -351,7 +325,7 @@ impl Client {
|
||||
|
||||
{
|
||||
if !imported_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(import_results);
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(&import_results);
|
||||
|
||||
if self.queue_info().is_empty() {
|
||||
self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted);
|
||||
@ -362,19 +336,50 @@ impl Client {
|
||||
invalid: invalid_blocks,
|
||||
enacted: enacted,
|
||||
retracted: retracted,
|
||||
sealed: Vec::new(),
|
||||
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if self.chain_info().best_block_hash != original_best {
|
||||
self.miner.update_sealing(self);
|
||||
}
|
||||
}
|
||||
|
||||
imported
|
||||
}
|
||||
|
||||
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &Bytes) -> ImportRoute where B: IsBlock + Drain {
|
||||
let number = block.header().number();
|
||||
// Are we committing an era?
|
||||
let ancient = if number >= HISTORY {
|
||||
let n = number - HISTORY;
|
||||
Some((n, self.chain.block_hash(n).unwrap()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Commit results
|
||||
let receipts = block.receipts().clone();
|
||||
let traces = From::from(block.traces().clone().unwrap_or_else(Vec::new));
|
||||
|
||||
// CHECK! I *think* this is fine, even if the state_root is equal to another
|
||||
// already-imported block of the same number.
|
||||
// TODO: Prove it with a test.
|
||||
block.drain().commit(number, 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)
|
||||
let route = self.chain.insert_block(block_data, receipts);
|
||||
self.tracedb.import(TraceImportRequest {
|
||||
traces: traces,
|
||||
block_hash: hash.clone(),
|
||||
block_number: number,
|
||||
enacted: route.enacted.clone(),
|
||||
retracted: route.retracted.len()
|
||||
});
|
||||
route
|
||||
}
|
||||
|
||||
/// Import transactions from the IO queue
|
||||
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
|
||||
let _timer = PerfTimer::new("import_queued_transactions");
|
||||
@ -830,6 +835,39 @@ impl MiningBlockChainClient for Client {
|
||||
fn vm_factory(&self) -> &EvmFactory {
|
||||
&self.vm_factory
|
||||
}
|
||||
|
||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
|
||||
let _import_lock = self.import_lock.lock();
|
||||
let _timer = PerfTimer::new("import_sealed_block");
|
||||
|
||||
let original_best = self.chain_info().best_block_hash;
|
||||
|
||||
let h = block.header().hash();
|
||||
let number = block.header().number();
|
||||
|
||||
let block_data = block.rlp_bytes();
|
||||
let route = self.commit_block(block, &h, &block_data);
|
||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||
|
||||
{
|
||||
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
|
||||
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
|
||||
|
||||
self.io_channel.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
|
||||
imported: vec![h.clone()],
|
||||
invalid: vec![],
|
||||
enacted: enacted,
|
||||
retracted: retracted,
|
||||
sealed: vec![h.clone()],
|
||||
})).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
|
||||
}
|
||||
|
||||
if self.chain_info().best_block_hash != original_best {
|
||||
self.miner.update_sealing(self);
|
||||
}
|
||||
|
||||
Ok(h)
|
||||
}
|
||||
}
|
||||
|
||||
impl MayPanic for Client {
|
||||
|
@ -37,7 +37,7 @@ use util::numbers::U256;
|
||||
use util::Itertools;
|
||||
use blockchain::TreeRoute;
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::OpenBlock;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
use header::{BlockNumber, Header};
|
||||
use transaction::{LocalizedTransaction, SignedTransaction};
|
||||
use log_entry::LocalizedLogEntry;
|
||||
@ -253,4 +253,7 @@ pub trait MiningBlockChainClient : BlockChainClient {
|
||||
|
||||
/// Returns EvmFactory.
|
||||
fn vm_factory(&self) -> &EvmFactory;
|
||||
|
||||
/// Import sealed block. Skips all verifications.
|
||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ use miner::{Miner, MinerService};
|
||||
use spec::Spec;
|
||||
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::OpenBlock;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
use executive::Executed;
|
||||
use error::{ExecutionError};
|
||||
use trace::LocalizedTrace;
|
||||
@ -248,6 +248,10 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
fn vm_factory(&self) -> &EvmFactory {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn import_sealed_block(&self, _block: SealedBlock) -> ImportResult {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockChainClient for TestBlockChainClient {
|
||||
|
@ -14,9 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ethash;
|
||||
|
||||
use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
|
||||
use ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
|
||||
use common::*;
|
||||
use block::*;
|
||||
use spec::CommonParams;
|
||||
|
@ -91,6 +91,8 @@ extern crate ethjson;
|
||||
extern crate bloomchain;
|
||||
#[macro_use] extern crate ethcore_ipc as ipc;
|
||||
extern crate rayon;
|
||||
extern crate hyper;
|
||||
extern crate ethash;
|
||||
pub extern crate ethstore;
|
||||
|
||||
#[cfg(test)] extern crate ethcore_devtools as devtools;
|
||||
|
@ -19,6 +19,7 @@ use std::sync::atomic::AtomicBool;
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
use util::*;
|
||||
use util::Colour::White;
|
||||
use account_provider::AccountProvider;
|
||||
use views::{BlockView, HeaderView};
|
||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics};
|
||||
@ -29,6 +30,7 @@ use receipt::{Receipt};
|
||||
use spec::Spec;
|
||||
use engine::Engine;
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
|
||||
use miner::work_notify::WorkPoster;
|
||||
|
||||
/// Different possible definitions for pending transaction set.
|
||||
#[derive(Debug)]
|
||||
@ -45,6 +47,8 @@ pub enum PendingSet {
|
||||
/// Configures the behaviour of the miner.
|
||||
#[derive(Debug)]
|
||||
pub struct MinerOptions {
|
||||
/// URLs to notify when there is new work.
|
||||
pub new_work_notify: Vec<String>,
|
||||
/// Force the miner to reseal, even when nobody has asked for work.
|
||||
pub force_sealing: bool,
|
||||
/// Reseal on receipt of new external transactions.
|
||||
@ -61,11 +65,14 @@ pub struct MinerOptions {
|
||||
pub pending_set: PendingSet,
|
||||
/// How many historical work packages can we store before running out?
|
||||
pub work_queue_size: usize,
|
||||
/// Can we submit two different solutions for the same block and expect both to result in an import?
|
||||
pub enable_resubmission: bool,
|
||||
}
|
||||
|
||||
impl Default for MinerOptions {
|
||||
fn default() -> Self {
|
||||
MinerOptions {
|
||||
new_work_notify: vec![],
|
||||
force_sealing: false,
|
||||
reseal_on_external_tx: true,
|
||||
reseal_on_own_tx: true,
|
||||
@ -74,6 +81,7 @@ impl Default for MinerOptions {
|
||||
pending_set: PendingSet::AlwaysQueue,
|
||||
reseal_min_period: Duration::from_secs(0),
|
||||
work_queue_size: 20,
|
||||
enable_resubmission: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,6 +103,7 @@ pub struct Miner {
|
||||
spec: Spec,
|
||||
|
||||
accounts: Option<Arc<AccountProvider>>,
|
||||
work_poster: Option<WorkPoster>,
|
||||
}
|
||||
|
||||
impl Miner {
|
||||
@ -112,23 +121,26 @@ impl Miner {
|
||||
extra_data: RwLock::new(Vec::new()),
|
||||
accounts: None,
|
||||
spec: spec,
|
||||
work_poster: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new instance of miner
|
||||
pub fn new(options: MinerOptions, spec: Spec, accounts: Option<Arc<AccountProvider>>) -> Arc<Miner> {
|
||||
let work_poster = if !options.new_work_notify.is_empty() { Some(WorkPoster::new(&options.new_work_notify)) } else { None };
|
||||
Arc::new(Miner {
|
||||
transaction_queue: Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, options.tx_gas_limit)),
|
||||
sealing_enabled: AtomicBool::new(options.force_sealing),
|
||||
sealing_enabled: AtomicBool::new(options.force_sealing || !options.new_work_notify.is_empty()),
|
||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||
sealing_block_last_request: Mutex::new(0),
|
||||
sealing_work: Mutex::new(UsingQueue::new(options.work_queue_size)),
|
||||
gas_range_target: RwLock::new((U256::zero(), U256::zero())),
|
||||
author: RwLock::new(Address::default()),
|
||||
extra_data: RwLock::new(Vec::new()),
|
||||
accounts: accounts,
|
||||
options: options,
|
||||
accounts: accounts,
|
||||
spec: spec,
|
||||
work_poster: work_poster,
|
||||
})
|
||||
}
|
||||
|
||||
@ -136,15 +148,20 @@ impl Miner {
|
||||
self.spec.engine.deref()
|
||||
}
|
||||
|
||||
fn forced_sealing(&self) -> bool {
|
||||
self.options.force_sealing || !self.options.new_work_notify.is_empty()
|
||||
}
|
||||
|
||||
/// Prepares new block for sealing including top transactions from queue.
|
||||
#[cfg_attr(feature="dev", allow(match_same_arms))]
|
||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||
fn prepare_sealing(&self, chain: &MiningBlockChainClient) {
|
||||
trace!(target: "miner", "prepare_sealing: entering");
|
||||
|
||||
let (transactions, mut open_block) = {
|
||||
let (transactions, mut open_block, last_work_hash) = {
|
||||
let transactions = {self.transaction_queue.lock().unwrap().top_transactions()};
|
||||
let mut sealing_work = self.sealing_work.lock().unwrap();
|
||||
let last_work_hash = sealing_work.peek_last_ref().map(|pb| pb.block().fields().header.hash());
|
||||
let best_hash = chain.best_block_header().sha3();
|
||||
/*
|
||||
// check to see if last ClosedBlock in would_seals is actually same parent block.
|
||||
@ -171,7 +188,7 @@ impl Miner {
|
||||
)
|
||||
}
|
||||
};
|
||||
(transactions, open_block)
|
||||
(transactions, open_block, last_work_hash)
|
||||
};
|
||||
|
||||
let mut invalid_transactions = HashSet::new();
|
||||
@ -237,13 +254,23 @@ impl Miner {
|
||||
}
|
||||
}
|
||||
|
||||
let work = {
|
||||
let mut sealing_work = self.sealing_work.lock().unwrap();
|
||||
if sealing_work.peek_last_ref().map_or(true, |pb| pb.block().fields().header.hash() != block.block().fields().header.hash()) {
|
||||
trace!(target: "miner", "Checking whether we need to reseal: last={:?}, this={:?}", last_work_hash, block.block().fields().header.hash());
|
||||
let work = if last_work_hash.map_or(true, |h| h != block.block().fields().header.hash()) {
|
||||
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
|
||||
let pow_hash = block.block().fields().header.hash();
|
||||
let number = block.block().fields().header.number();
|
||||
let difficulty = *block.block().fields().header.difficulty();
|
||||
sealing_work.push(block);
|
||||
}
|
||||
|
||||
Some((pow_hash, difficulty, number))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash()));
|
||||
work
|
||||
};
|
||||
work.map(|(pow_hash, difficulty, number)| self.work_poster.as_ref().map(|ref p| p.notify(pow_hash, difficulty, number)));
|
||||
}
|
||||
|
||||
fn update_gas_limit(&self, chain: &MiningBlockChainClient) {
|
||||
@ -562,7 +589,7 @@ impl MinerService for Miner {
|
||||
let current_no = chain.chain_info().best_block_number;
|
||||
let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions();
|
||||
let last_request = *self.sealing_block_last_request.lock().unwrap();
|
||||
let should_disable_sealing = !self.options.force_sealing
|
||||
let should_disable_sealing = !self.forced_sealing()
|
||||
&& !has_local_transactions
|
||||
&& current_no > last_request
|
||||
&& current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS;
|
||||
@ -589,26 +616,22 @@ impl MinerService for Miner {
|
||||
}
|
||||
|
||||
fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
||||
if let Some(b) = self.sealing_work.lock().unwrap().take_used_if(|b| &b.hash() == &pow_hash) {
|
||||
match b.lock().try_seal(self.engine(), seal) {
|
||||
Err(_) => {
|
||||
info!(target: "miner", "Mined block rejected, PoW was invalid.");
|
||||
let result = if let Some(b) = self.sealing_work.lock().unwrap().get_used_if(if self.options.enable_resubmission { GetAction::Clone } else { GetAction::Take }, |b| &b.hash() == &pow_hash) {
|
||||
b.lock().try_seal(self.engine(), seal).or_else(|_| {
|
||||
warn!(target: "miner", "Mined solution rejected: Invalid.");
|
||||
Err(Error::PowInvalid)
|
||||
}
|
||||
Ok(sealed) => {
|
||||
info!(target: "miner", "New block mined, hash: {}", sealed.header().hash());
|
||||
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
|
||||
let b = sealed.rlp_bytes();
|
||||
let h = b.sha3();
|
||||
try!(chain.import_block(b));
|
||||
info!("Block {} submitted and imported.", h);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
info!(target: "miner", "Mined block rejected, PoW hash invalid or out of date.");
|
||||
warn!(target: "miner", "Mined solution rejected: Block unknown or out of date.");
|
||||
Err(Error::PowHashInvalid)
|
||||
}
|
||||
};
|
||||
result.and_then(|sealed| {
|
||||
let n = sealed.header().number();
|
||||
let h = sealed.header().hash();
|
||||
try!(chain.import_sealed_block(sealed));
|
||||
info!(target: "miner", "Mined block imported OK. #{}: {}", paint(White.bold(), format!("{}", n)), paint(White.bold(), h.hex()));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) {
|
||||
|
@ -45,6 +45,7 @@
|
||||
mod miner;
|
||||
mod external;
|
||||
mod transaction_queue;
|
||||
mod work_notify;
|
||||
|
||||
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin};
|
||||
pub use self::miner::{Miner, MinerOptions, PendingSet};
|
||||
|
115
ethcore/src/miner/work_notify.rs
Normal file
115
ethcore/src/miner/work_notify.rs
Normal file
@ -0,0 +1,115 @@
|
||||
// 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 hyper;
|
||||
|
||||
use hyper::header::ContentType;
|
||||
use hyper::method::Method;
|
||||
use hyper::client::{Request, Response, Client};
|
||||
use hyper::{Next};
|
||||
use hyper::net::HttpStream;
|
||||
use ethash::SeedHashCompute;
|
||||
use hyper::Url;
|
||||
use util::*;
|
||||
use ethereum::ethash::Ethash;
|
||||
|
||||
pub struct WorkPoster {
|
||||
urls: Vec<Url>,
|
||||
client: Mutex<Client<PostHandler>>,
|
||||
seed_compute: Mutex<SeedHashCompute>,
|
||||
}
|
||||
|
||||
impl WorkPoster {
|
||||
pub fn new(urls: &[String]) -> Self {
|
||||
let urls = urls.into_iter().filter_map(|u| {
|
||||
match Url::parse(&u) {
|
||||
Ok(url) => Some(url),
|
||||
Err(e) => {
|
||||
warn!("Error parsing URL {} : {}", u, e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
let client = WorkPoster::create_client();
|
||||
WorkPoster {
|
||||
client: Mutex::new(client),
|
||||
urls: urls,
|
||||
seed_compute: Mutex::new(SeedHashCompute::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_client() -> Client<PostHandler> {
|
||||
let client = Client::<PostHandler>::configure()
|
||||
.keep_alive(true)
|
||||
.build().expect("Error creating HTTP client") as Client<PostHandler>;
|
||||
client
|
||||
}
|
||||
|
||||
pub fn notify(&self, pow_hash: H256, difficulty: U256, number: u64) {
|
||||
// TODO: move this to engine
|
||||
let target = Ethash::difficulty_to_boundary(&difficulty);
|
||||
let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(number);
|
||||
let seed_hash = H256::from_slice(&seed_hash[..]);
|
||||
let body = format!(r#"{{ "result": ["0x{}","0x{}","0x{}","0x{:x}"] }}"#,
|
||||
pow_hash.hex(), seed_hash.hex(), target.hex(), number);
|
||||
let mut client = self.client.lock().unwrap();
|
||||
for u in &self.urls {
|
||||
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {
|
||||
warn!("Error sending HTTP notification to {} : {}, retrying", u, e);
|
||||
// TODO: remove this once https://github.com/hyperium/hyper/issues/848 is fixed
|
||||
*client = WorkPoster::create_client();
|
||||
if let Err(e) = client.request(u.clone(), PostHandler { body: body.clone() }) {
|
||||
warn!("Error sending HTTP notification to {} : {}", u, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PostHandler {
|
||||
body: String,
|
||||
}
|
||||
|
||||
impl hyper::client::Handler<HttpStream> for PostHandler {
|
||||
fn on_request(&mut self, request: &mut Request) -> Next {
|
||||
request.set_method(Method::Post);
|
||||
request.headers_mut().set(ContentType::json());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_writable(&mut self, encoder: &mut hyper::Encoder<HttpStream>) -> Next {
|
||||
if let Err(e) = encoder.write_all(self.body.as_bytes()) {
|
||||
trace!("Error posting work data: {}", e);
|
||||
}
|
||||
encoder.close();
|
||||
Next::read()
|
||||
|
||||
}
|
||||
|
||||
fn on_response(&mut self, _response: Response) -> Next {
|
||||
Next::end()
|
||||
}
|
||||
|
||||
fn on_response_readable(&mut self, _decoder: &mut hyper::Decoder<HttpStream>) -> Next {
|
||||
Next::end()
|
||||
}
|
||||
|
||||
fn on_error(&mut self, err: hyper::Error) -> Next {
|
||||
trace!("Error posting work data: {}", err);
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
//! Creates and registers client and network services.
|
||||
|
||||
use util::*;
|
||||
use util::Colour::{Yellow, White};
|
||||
use util::panics::*;
|
||||
use spec::Spec;
|
||||
use error::*;
|
||||
@ -36,6 +37,8 @@ pub enum SyncMessage {
|
||||
retracted: Vec<H256>,
|
||||
/// Hashes of blocks that are now included in cannonical chain
|
||||
enacted: Vec<H256>,
|
||||
/// Hashes of blocks that are sealed by this node
|
||||
sealed: Vec<H256>,
|
||||
},
|
||||
/// Best Block Hash in chain has been changed
|
||||
NewChainHead,
|
||||
@ -69,8 +72,7 @@ impl ClientService {
|
||||
try!(net_service.start());
|
||||
}
|
||||
|
||||
info!("Starting {}", net_service.host_info());
|
||||
info!("Configured for {} using {:?} engine", spec.name, spec.engine.name());
|
||||
info!("Configured for {} using {} engine", paint(White.bold(), spec.name.clone()), paint(Yellow.bold(), spec.engine.name().to_owned()));
|
||||
let client = try!(Client::new(config, spec, db_path, miner, net_service.io().channel()));
|
||||
panic_handler.forward_from(client.deref());
|
||||
let client_io = Arc::new(ClientIoHandler {
|
||||
|
@ -17,7 +17,7 @@
|
||||
use client::{BlockChainClient, Client, ClientConfig};
|
||||
use common::*;
|
||||
use spec::*;
|
||||
use block::{OpenBlock};
|
||||
use block::{OpenBlock, Drain};
|
||||
use blockchain::{BlockChain, Config as BlockChainConfig};
|
||||
use state::*;
|
||||
use evm::Schedule;
|
||||
|
@ -169,6 +169,12 @@ Sealing/Mining Options:
|
||||
more than 32 characters.
|
||||
--tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting
|
||||
to be included in next block) [default: 1024].
|
||||
--remove-solved Move solved blocks from the work package queue
|
||||
instead of cloning them. This gives a slightly
|
||||
faster import speed, but means that extra solutions
|
||||
submitted for the same work package will go unused.
|
||||
--notify-work URLS URLs to which work package notifications are pushed.
|
||||
URLS should be a comma-delimited list of HTTP URLs.
|
||||
|
||||
Footprint Options:
|
||||
--tracing BOOL Indicates if full transaction tracing should be
|
||||
@ -311,6 +317,7 @@ pub struct Args {
|
||||
pub flag_reseal_on_txs: String,
|
||||
pub flag_reseal_min_period: u64,
|
||||
pub flag_work_queue_size: usize,
|
||||
pub flag_remove_solved: bool,
|
||||
pub flag_tx_gas_limit: Option<String>,
|
||||
pub flag_relay_set: String,
|
||||
pub flag_author: Option<String>,
|
||||
@ -320,6 +327,7 @@ pub struct Args {
|
||||
pub flag_gas_cap: String,
|
||||
pub flag_extra_data: Option<String>,
|
||||
pub flag_tx_queue_size: usize,
|
||||
pub flag_notify_work: Option<String>,
|
||||
pub flag_logging: Option<String>,
|
||||
pub flag_version: bool,
|
||||
pub flag_from: String,
|
||||
|
@ -25,6 +25,7 @@ use docopt::Docopt;
|
||||
|
||||
use die::*;
|
||||
use util::*;
|
||||
use util::log::Colour::*;
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use util::network_settings::NetworkSettings;
|
||||
use ethcore::client::{append_path, get_db_path, ClientConfig, DatabaseCompactionProfile, Switch, VMType};
|
||||
@ -84,6 +85,10 @@ impl Configuration {
|
||||
)
|
||||
}
|
||||
|
||||
fn work_notify(&self) -> Vec<String> {
|
||||
self.args.flag_notify_work.as_ref().map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect())
|
||||
}
|
||||
|
||||
pub fn miner_options(&self) -> MinerOptions {
|
||||
let (own, ext) = match self.args.flag_reseal_on_txs.as_str() {
|
||||
"none" => (false, false),
|
||||
@ -93,6 +98,7 @@ impl Configuration {
|
||||
x => die!("{}: Invalid value for --reseal option. Use --help for more information.", x)
|
||||
};
|
||||
MinerOptions {
|
||||
new_work_notify: self.work_notify(),
|
||||
force_sealing: self.args.flag_force_sealing,
|
||||
reseal_on_external_tx: ext,
|
||||
reseal_on_own_tx: own,
|
||||
@ -106,6 +112,7 @@ impl Configuration {
|
||||
},
|
||||
reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period),
|
||||
work_queue_size: self.args.flag_work_queue_size,
|
||||
enable_resubmission: !self.args.flag_remove_solved,
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,7 +182,7 @@ impl Configuration {
|
||||
let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
|
||||
let gas_per_tx: f32 = 21000.0;
|
||||
let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
|
||||
info!("Using a conversion rate of Ξ1 = US${} ({} wei/gas)", usd_per_eth, wei_per_gas);
|
||||
info!("Using a conversion rate of Ξ1 = {} ({} wei/gas)", paint(White.bold(), format!("US${}", usd_per_eth)), paint(Yellow.bold(), format!("{}", wei_per_gas)));
|
||||
U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use ctrlc::CtrlC;
|
||||
use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError};
|
||||
use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, paint, Colour, version};
|
||||
use util::panics::{MayPanic, ForwardPanic, PanicHandler};
|
||||
use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path};
|
||||
use ethcore::error::{Error, ImportError};
|
||||
@ -184,10 +184,12 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig)
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
||||
// Setup logging
|
||||
let logger = setup_log::setup_log(&conf.args.flag_logging);
|
||||
let logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color());
|
||||
// Raise fdlimit
|
||||
unsafe { ::fdlimit::raise_fd_limit(); }
|
||||
|
||||
info!("Starting {}", paint(Colour::White.bold(), format!("{}", version())));
|
||||
|
||||
let net_settings = conf.net_settings(&spec);
|
||||
let sync_config = conf.sync_config(&spec);
|
||||
|
||||
@ -320,6 +322,8 @@ fn execute_export(conf: Configuration) {
|
||||
// Setup panic handler
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
||||
// Setup logging
|
||||
let _logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color());
|
||||
// Raise fdlimit
|
||||
unsafe { ::fdlimit::raise_fd_limit(); }
|
||||
|
||||
@ -392,6 +396,8 @@ fn execute_import(conf: Configuration) {
|
||||
// Setup panic handler
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
||||
// Setup logging
|
||||
let _logger = setup_log::setup_log(&conf.args.flag_logging, conf.have_color());
|
||||
// Raise fdlimit
|
||||
unsafe { ::fdlimit::raise_fd_limit(); }
|
||||
|
||||
|
@ -19,10 +19,10 @@ use std::env;
|
||||
use std::sync::Arc;
|
||||
use time;
|
||||
use env_logger::LogBuilder;
|
||||
use util::{RotatingLogger};
|
||||
use util::RotatingLogger;
|
||||
|
||||
/// Sets up the logger
|
||||
pub fn setup_log(init: &Option<String>) -> Arc<RotatingLogger> {
|
||||
pub fn setup_log(init: &Option<String>, enable_color: bool) -> Arc<RotatingLogger> {
|
||||
use rlog::*;
|
||||
|
||||
let mut levels = String::new();
|
||||
@ -43,7 +43,7 @@ pub fn setup_log(init: &Option<String>) -> Arc<RotatingLogger> {
|
||||
builder.parse(s);
|
||||
}
|
||||
|
||||
let logs = Arc::new(RotatingLogger::new(levels));
|
||||
let logs = Arc::new(RotatingLogger::new(levels, enable_color));
|
||||
let logger = logs.clone();
|
||||
let format = move |record: &LogRecord| {
|
||||
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap();
|
||||
|
@ -498,7 +498,7 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
|
||||
let pow_hash = b.hash();
|
||||
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
|
||||
let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(b.block().header().number());
|
||||
to_value(&(pow_hash, H256::from_slice(&seed_hash[..]), target))
|
||||
to_value(&(pow_hash, H256::from_slice(&seed_hash[..]), target, &U256::from(b.block().header().number())))
|
||||
}).unwrap_or(Err(Error::internal_error())) // no work found.
|
||||
},
|
||||
_ => Err(Error::invalid_params())
|
||||
|
@ -52,6 +52,7 @@ fn sync_provider() -> Arc<TestSyncProvider> {
|
||||
fn miner_service(spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
|
||||
Miner::new(
|
||||
MinerOptions {
|
||||
new_work_notify: vec![],
|
||||
force_sealing: true,
|
||||
reseal_on_external_tx: true,
|
||||
reseal_on_own_tx: true,
|
||||
@ -60,6 +61,7 @@ fn miner_service(spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
|
||||
pending_set: PendingSet::SealingOrElseQueue,
|
||||
reseal_min_period: Duration::from_secs(0),
|
||||
work_queue_size: 50,
|
||||
enable_resubmission: true,
|
||||
},
|
||||
spec,
|
||||
Some(accounts)
|
||||
|
@ -32,7 +32,7 @@ fn client_service() -> Arc<TestBlockChainClient> {
|
||||
}
|
||||
|
||||
fn logger() -> Arc<RotatingLogger> {
|
||||
Arc::new(RotatingLogger::new("rpc=trace".to_owned()))
|
||||
Arc::new(RotatingLogger::new("rpc=trace".to_owned(), false))
|
||||
}
|
||||
|
||||
fn settings() -> Arc<NetworkSettings> {
|
||||
|
@ -120,7 +120,7 @@ impl<T: TimeProvider> AuthCodes<T> {
|
||||
.filter_map(|f| String::from_utf8(f.to_vec()).ok())
|
||||
.collect::<Vec<String>>()
|
||||
.join("-");
|
||||
info!(target: "signer", "New authentication token generated.");
|
||||
trace!(target: "signer", "New authentication token generated.");
|
||||
self.codes.push(code);
|
||||
Ok(readable_code)
|
||||
}
|
||||
|
@ -1231,6 +1231,14 @@ impl ChainSync {
|
||||
rlp_stream.out()
|
||||
}
|
||||
|
||||
/// creates latest block rlp for the given client
|
||||
fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes {
|
||||
let mut rlp_stream = RlpStream::new_list(2);
|
||||
rlp_stream.append_raw(&chain.block(BlockID::Hash(hash.clone())).expect("Block has just been sealed; qed"), 1);
|
||||
rlp_stream.append(&chain.block_total_difficulty(BlockID::Hash(hash.clone())).expect("Block has just been sealed; qed."));
|
||||
rlp_stream.out()
|
||||
}
|
||||
|
||||
/// returns peer ids that have less blocks than our chain
|
||||
fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &SyncIo) -> Vec<(PeerId, BlockNumber)> {
|
||||
let latest_hash = chain_info.best_block_hash;
|
||||
@ -1250,7 +1258,6 @@ impl ChainSync {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
|
||||
fn select_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> Vec<(PeerId, BlockNumber)> {
|
||||
use rand::Rng;
|
||||
let mut lagging_peers = self.get_lagging_peers(chain_info, io);
|
||||
@ -1263,13 +1270,24 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
/// propagates latest block to lagging peers
|
||||
fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> usize {
|
||||
let lucky_peers = self.select_lagging_peers(chain_info, io);
|
||||
fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo, sealed: &[H256]) -> usize {
|
||||
let lucky_peers: Vec<_> = if sealed.is_empty() {
|
||||
self.select_lagging_peers(chain_info, io).iter().map(|&(id, _)| id).collect()
|
||||
} else {
|
||||
self.peers.keys().cloned().collect()
|
||||
};
|
||||
trace!(target: "sync", "Sending NewBlocks to {:?}", lucky_peers);
|
||||
let mut sent = 0;
|
||||
for (peer_id, _) in lucky_peers {
|
||||
for peer_id in lucky_peers {
|
||||
if sealed.is_empty() {
|
||||
let rlp = ChainSync::create_latest_block_rlp(io.chain());
|
||||
self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp);
|
||||
} else {
|
||||
for h in sealed {
|
||||
let rlp = ChainSync::create_new_block_rlp(io.chain(), h);
|
||||
self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp);
|
||||
}
|
||||
}
|
||||
self.peers.get_mut(&peer_id).unwrap().latest_hash = chain_info.best_block_hash.clone();
|
||||
self.peers.get_mut(&peer_id).unwrap().latest_number = Some(chain_info.best_block_number);
|
||||
sent += 1;
|
||||
@ -1346,11 +1364,11 @@ impl ChainSync {
|
||||
sent
|
||||
}
|
||||
|
||||
fn propagate_latest_blocks(&mut self, io: &mut SyncIo) {
|
||||
fn propagate_latest_blocks(&mut self, io: &mut SyncIo, sealed: &[H256]) {
|
||||
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 hashes = self.propagate_new_hashes(&chain_info, io);
|
||||
let blocks = self.propagate_blocks(&chain_info, io);
|
||||
let blocks = self.propagate_blocks(&chain_info, io, sealed);
|
||||
if blocks != 0 || hashes != 0 {
|
||||
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
|
||||
}
|
||||
@ -1365,10 +1383,10 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
/// called when block is imported to chain, updates transactions queue and propagates the blocks
|
||||
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
|
||||
pub fn chain_new_blocks(&mut self, io: &mut SyncIo, _imported: &[H256], invalid: &[H256], _enacted: &[H256], _retracted: &[H256], sealed: &[H256]) {
|
||||
if io.is_chain_queue_empty() {
|
||||
// Propagate latests blocks
|
||||
self.propagate_latest_blocks(io);
|
||||
self.propagate_latest_blocks(io, sealed);
|
||||
}
|
||||
if !invalid.is_empty() {
|
||||
trace!(target: "sync", "Bad blocks in the queue, restarting");
|
||||
@ -1637,7 +1655,26 @@ mod tests {
|
||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client);
|
||||
let chain_info = client.chain_info();
|
||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||
let peer_count = sync.propagate_blocks(&chain_info, &mut io);
|
||||
let peer_count = sync.propagate_blocks(&chain_info, &mut io, &[]);
|
||||
|
||||
// 1 message should be send
|
||||
assert_eq!(1, io.queue.len());
|
||||
// 1 peer should be updated
|
||||
assert_eq!(1, peer_count);
|
||||
// NEW_BLOCK_PACKET
|
||||
assert_eq!(0x07, io.queue[0].packet_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sends_sealed_block() {
|
||||
let mut client = TestBlockChainClient::new();
|
||||
client.add_blocks(100, EachBlockWith::Uncle);
|
||||
let mut queue = VecDeque::new();
|
||||
let hash = client.block_hash(BlockID::Number(99)).unwrap();
|
||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client);
|
||||
let chain_info = client.chain_info();
|
||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||
let peer_count = sync.propagate_blocks(&chain_info, &mut io, &[hash.clone()]);
|
||||
|
||||
// 1 message should be send
|
||||
assert_eq!(1, io.queue.len());
|
||||
@ -1761,7 +1798,7 @@ mod tests {
|
||||
let chain_info = client.chain_info();
|
||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||
|
||||
sync.propagate_blocks(&chain_info, &mut io);
|
||||
sync.propagate_blocks(&chain_info, &mut io, &[]);
|
||||
|
||||
let data = &io.queue[0].data.clone();
|
||||
let result = sync.on_peer_new_block(&mut io, 0, &UntrustedRlp::new(data));
|
||||
@ -1794,7 +1831,7 @@ mod tests {
|
||||
let mut queue = VecDeque::new();
|
||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||
io.chain.miner.chain_new_blocks(io.chain, &[], &[], &[], &good_blocks);
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks);
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[]);
|
||||
assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0);
|
||||
assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 1);
|
||||
}
|
||||
@ -1808,7 +1845,7 @@ mod tests {
|
||||
let mut queue = VecDeque::new();
|
||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||
io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks);
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks);
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[]);
|
||||
}
|
||||
|
||||
// then
|
||||
@ -1833,10 +1870,10 @@ mod tests {
|
||||
let mut io = TestIo::new(&mut client, &mut queue, None);
|
||||
|
||||
// when
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks);
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[]);
|
||||
assert_eq!(io.chain.miner.status().transactions_in_future_queue, 0);
|
||||
assert_eq!(io.chain.miner.status().transactions_in_pending_queue, 0);
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks);
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[]);
|
||||
|
||||
// then
|
||||
let status = io.chain.miner.status();
|
||||
|
@ -196,9 +196,9 @@ impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
||||
#[cfg_attr(feature="dev", allow(single_match))]
|
||||
fn message(&self, io: &NetworkContext<SyncMessage>, message: &SyncMessage) {
|
||||
match *message {
|
||||
SyncMessage::NewChainBlocks { ref imported, ref invalid, ref enacted, ref retracted } => {
|
||||
SyncMessage::NewChainBlocks { ref imported, ref invalid, ref enacted, ref retracted, ref sealed } => {
|
||||
let mut sync_io = NetSyncIo::new(io, self.chain.deref());
|
||||
self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted);
|
||||
self.sync.write().unwrap().chain_new_blocks(&mut sync_io, imported, invalid, enacted, retracted, sealed);
|
||||
},
|
||||
_ => {/* Ignore other messages */},
|
||||
}
|
||||
|
@ -173,6 +173,6 @@ impl TestNet {
|
||||
|
||||
pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) {
|
||||
let mut peer = self.peer_mut(peer_id);
|
||||
peer.sync.write().unwrap().chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[], &[]);
|
||||
peer.sync.write().unwrap().chain_new_blocks(&mut TestIo::new(&mut peer.chain, &mut peer.queue, None), &[], &[], &[], &[], &[]);
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ vergen = "0.1"
|
||||
target_info = "0.1"
|
||||
bigint = { path = "bigint" }
|
||||
chrono = "0.2"
|
||||
ansi_term = "0.7"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -99,7 +99,7 @@ impl DatabaseConfig {
|
||||
DatabaseConfig {
|
||||
cache_size: Some(cache_size),
|
||||
prefix_size: None,
|
||||
max_open_files: -1,
|
||||
max_open_files: 256,
|
||||
compaction: CompactionProfile::default(),
|
||||
}
|
||||
}
|
||||
@ -122,7 +122,7 @@ impl Default for DatabaseConfig {
|
||||
DatabaseConfig {
|
||||
cache_size: None,
|
||||
prefix_size: None,
|
||||
max_open_files: -1,
|
||||
max_open_files: 256,
|
||||
compaction: CompactionProfile::default(),
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,7 @@ extern crate libc;
|
||||
extern crate target_info;
|
||||
extern crate bigint;
|
||||
extern crate chrono;
|
||||
extern crate ansi_term;
|
||||
|
||||
pub mod standard;
|
||||
#[macro_use]
|
||||
|
@ -20,7 +20,21 @@ use std::env;
|
||||
use rlog::{LogLevelFilter};
|
||||
use env_logger::LogBuilder;
|
||||
use std::sync::{RwLock, RwLockReadGuard};
|
||||
use std::sync::atomic::{Ordering, AtomicBool};
|
||||
use arrayvec::ArrayVec;
|
||||
pub use ansi_term::{Colour, Style};
|
||||
|
||||
lazy_static! {
|
||||
static ref USE_COLOR: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
|
||||
/// Paint, using colour if desired.
|
||||
pub fn paint(c: Style, t: String) -> String {
|
||||
match USE_COLOR.load(Ordering::Relaxed) {
|
||||
true => format!("{}", c.paint(t)),
|
||||
false => t,
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref LOG_DUMMY: bool = {
|
||||
@ -57,7 +71,8 @@ impl RotatingLogger {
|
||||
|
||||
/// Creates new `RotatingLogger` with given levels.
|
||||
/// It does not enforce levels - it's just read only.
|
||||
pub fn new(levels: String) -> Self {
|
||||
pub fn new(levels: String, enable_color: bool) -> Self {
|
||||
USE_COLOR.store(enable_color, Ordering::Relaxed);
|
||||
RotatingLogger {
|
||||
levels: levels,
|
||||
logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()),
|
||||
@ -86,7 +101,7 @@ mod test {
|
||||
use super::RotatingLogger;
|
||||
|
||||
fn logger() -> RotatingLogger {
|
||||
RotatingLogger::new("test".to_owned())
|
||||
RotatingLogger::new("test".to_owned(), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -32,6 +32,8 @@ use misc::version;
|
||||
use crypto::*;
|
||||
use sha3::Hashable;
|
||||
use rlp::*;
|
||||
use log::Colour::White;
|
||||
use log::paint;
|
||||
use network::session::{Session, SessionData};
|
||||
use error::*;
|
||||
use io::*;
|
||||
@ -343,6 +345,7 @@ pub struct Host<Message> where Message: Send + Sync + Clone {
|
||||
reserved_nodes: RwLock<HashSet<NodeId>>,
|
||||
num_sessions: AtomicUsize,
|
||||
stopping: AtomicBool,
|
||||
first_time: AtomicBool,
|
||||
}
|
||||
|
||||
impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
||||
@ -398,6 +401,7 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
||||
reserved_nodes: RwLock::new(HashSet::new()),
|
||||
num_sessions: AtomicUsize::new(0),
|
||||
stopping: AtomicBool::new(false),
|
||||
first_time: AtomicBool::new(true),
|
||||
};
|
||||
|
||||
for n in boot_nodes {
|
||||
@ -533,7 +537,11 @@ impl<Message> Host<Message> where Message: Send + Sync + Clone {
|
||||
};
|
||||
|
||||
self.info.write().unwrap().public_endpoint = Some(public_endpoint.clone());
|
||||
info!("Public node URL: {}", self.external_url().unwrap());
|
||||
|
||||
if self.first_time.load(AtomicOrdering::Relaxed) {
|
||||
info!("Public node URL: {}", paint(White.bold(), format!("{}", self.external_url().unwrap())));
|
||||
self.first_time.store(false, AtomicOrdering::Relaxed);
|
||||
}
|
||||
|
||||
// Initialize discovery.
|
||||
let discovery = {
|
||||
|
@ -88,7 +88,7 @@ impl NetworkProtocolHandler<TestProtocolMessage> for TestProtocol {
|
||||
|
||||
/// Timer function called after a timeout created with `NetworkContext::timeout`.
|
||||
fn timeout(&self, io: &NetworkContext<TestProtocolMessage>, timer: TimerToken) {
|
||||
io.message(TestProtocolMessage { payload: 22 });
|
||||
io.message(TestProtocolMessage { payload: 22 }).unwrap();
|
||||
assert_eq!(timer, 0);
|
||||
self.got_timeout.store(true, AtomicOrdering::Relaxed);
|
||||
}
|
||||
|
@ -27,6 +27,14 @@ pub struct UsingQueue<T> where T: Clone {
|
||||
max_size: usize,
|
||||
}
|
||||
|
||||
/// Take an item or just clone it?
|
||||
pub enum GetAction {
|
||||
/// Remove the item, faster but you can't get it back.
|
||||
Take,
|
||||
/// Clone the item, slower but you can get it again.
|
||||
Clone,
|
||||
}
|
||||
|
||||
impl<T> UsingQueue<T> where T: Clone {
|
||||
/// Create a new struct with a maximum size of `max_size`.
|
||||
pub fn new(max_size: usize) -> UsingQueue<T> {
|
||||
@ -74,6 +82,20 @@ impl<T> UsingQueue<T> where T: Clone {
|
||||
self.in_use.iter().position(|r| predicate(r)).map(|i| self.in_use.remove(i))
|
||||
}
|
||||
|
||||
/// Returns `Some` item which is the first that `f` returns `true` with a reference to it
|
||||
/// as a parameter or `None` if no such item exists in the queue.
|
||||
pub fn clone_used_if<P>(&mut self, predicate: P) -> Option<T> where P: Fn(&T) -> bool {
|
||||
self.in_use.iter().find(|r| predicate(r)).cloned()
|
||||
}
|
||||
|
||||
/// Fork-function for `take_used_if` and `clone_used_if`.
|
||||
pub fn get_used_if<P>(&mut self, action: GetAction, predicate: P) -> Option<T> where P: Fn(&T) -> bool {
|
||||
match action {
|
||||
GetAction::Take => self.take_used_if(predicate),
|
||||
GetAction::Clone => self.clone_used_if(predicate),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the most recently pushed block if `f` returns `true` with a reference to it as
|
||||
/// a parameter, otherwise `None`.
|
||||
/// Will not destroy a block if a reference to it has previously been returned by `use_last_ref`,
|
||||
@ -94,18 +116,66 @@ impl<T> UsingQueue<T> where T: Clone {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_find_when_pushed() {
|
||||
fn should_not_find_when_pushed() {
|
||||
let mut q = UsingQueue::new(2);
|
||||
q.push(1);
|
||||
assert!(q.take_used_if(|i| i == &1).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_find_when_pushed_with_clone() {
|
||||
let mut q = UsingQueue::new(2);
|
||||
q.push(1);
|
||||
assert!(q.clone_used_if(|i| i == &1).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_find_when_pushed_and_used() {
|
||||
let mut q = UsingQueue::new(2);
|
||||
q.push(1);
|
||||
q.use_last_ref();
|
||||
assert!(q.take_used_if(|i| i == &1).is_some());
|
||||
assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_have_same_semantics_for_get_take_clone() {
|
||||
let mut q = UsingQueue::new(2);
|
||||
q.push(1);
|
||||
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none());
|
||||
assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none());
|
||||
q.use_last_ref();
|
||||
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1);
|
||||
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).unwrap() == 1);
|
||||
assert!(q.get_used_if(GetAction::Take, |i| i == &1).unwrap() == 1);
|
||||
assert!(q.get_used_if(GetAction::Clone, |i| i == &1).is_none());
|
||||
assert!(q.get_used_if(GetAction::Take, |i| i == &1).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_find_when_pushed_and_used_with_clone() {
|
||||
let mut q = UsingQueue::new(2);
|
||||
q.push(1);
|
||||
q.use_last_ref();
|
||||
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_find_again_when_pushed_and_taken() {
|
||||
let mut q = UsingQueue::new(2);
|
||||
q.push(1);
|
||||
q.use_last_ref();
|
||||
assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
|
||||
assert!(q.clone_used_if(|i| i == &1).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_find_again_when_pushed_and_cloned() {
|
||||
let mut q = UsingQueue::new(2);
|
||||
q.push(1);
|
||||
q.use_last_ref();
|
||||
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
|
||||
assert!(q.clone_used_if(|i| i == &1).unwrap() == 1);
|
||||
assert!(q.take_used_if(|i| i == &1).unwrap() == 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
Reference in New Issue
Block a user