Merge branch 'master' into tx_queue_invalid

This commit is contained in:
Tomasz Drwięga 2016-03-18 09:44:31 +01:00
commit 48c72a168c
16 changed files with 376 additions and 134 deletions

View File

@ -8,20 +8,43 @@ branches:
- /^stable-.*$/ - /^stable-.*$/
- /^beta$/ - /^beta$/
- /^stable$/ - /^stable$/
git:
depth: 3
matrix: matrix:
fast_finish: false fast_finish: true
allow_failures: allow_failures:
- rust: nightly - rust: nightly
include: include:
- rust: stable - rust: stable
env: FEATURES="--features travis-beta" KCOV_FEATURES="" TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer" ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}" env: FEATURES="--features travis-beta" RUN_TESTS="true"
# - rust: beta
# env: FEATURES="--features travis-beta" RUN_TESTS="true"
- rust: stable
env: FEATURES="--features travis-beta" RUN_BUILD="true"
- rust: beta
env: FEATURES="--features travis-beta" RUN_BUILD="true"
- rust: stable
env: FEATURES="--features travis-beta" RUN_COVERAGE="true"
# - rust: nightly
# env: FEATURES="--features travis-nightly" RUN_BENCHES="true"
- rust: nightly
env: FEATURES="--features travis-nightly" RUN_TESTS="true"
env:
global:
# GH_TOKEN
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
- TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer"
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- KCOV_FEATURES=""
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov"
- RUN_TESTS="false"
- RUN_COVERAGE="false"
- RUN_BUILD="false"
- RUN_BENCHES="false"
cache: cache:
apt: true apt: true
directories: directories:
- target/debug/deps - $TRAVIS_BUILD_DIR/target
- target/debug/build
- target/release/deps
- target/release/build
- $HOME/.cargo - $HOME/.cargo
addons: addons:
apt: apt:
@ -29,22 +52,25 @@ addons:
- libcurl4-openssl-dev - libcurl4-openssl-dev
- libelf-dev - libelf-dev
- libdw-dev - libdw-dev
script: script:
- cargo build --release --verbose ${FEATURES} - if [ "$RUN_TESTS" = "true" ]; then cargo test --release --verbose ${FEATURES} ${TARGETS}; fi
- cargo test --release --verbose ${FEATURES} ${TARGETS} - if [ "$RUN_BENCHES" = "true" ]; then cargo bench --no-run ${FEATURES} ${TARGETS}; fi
#- cargo bench --no-run ${FEATURES} ${TARGETS} - if [ "$RUN_BUILD" = "true" ]; then cargo build --release --verbose ${FEATURES}; fi
- tar cvzf parity${ARCHIVE_SUFFIX}.tar.gz -C target/release parity - if [ "$RUN_BUILD" = "true" ]; then tar cvzf parity${ARCHIVE_SUFFIX}.tar.gz -C target/release parity; fi
after_success: | after_success: |
[ "$RUN_COVERAGE" = "true" ] &&
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && make install DESTDIR=../tmp && cd ../.. && tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && make install DESTDIR=../tmp && cd ../.. &&
cargo test --no-run ${KCOV_FEATURES} ${TARGETS} && cargo test --no-run ${KCOV_FEATURES} ${TARGETS} &&
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethcore_util-* && $KCOV_CMD target/debug/deps/ethcore_util-* &&
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethash-* && $KCOV_CMD target/debug/deps/ethash-* &&
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethcore-* && $KCOV_CMD target/debug/deps/ethcore-* &&
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethsync-* && $KCOV_CMD target/debug/deps/ethsync-* &&
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethcore_rpc-* && $KCOV_CMD target/debug/deps/ethcore_rpc-* &&
./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov target/debug/deps/ethminer-* && $KCOV_CMD target/debug/deps/ethminer-* &&
./kcov-master/tmp/usr/local/bin/kcov --coveralls-id=${TRAVIS_JOB_ID} --exclude-pattern /usr/,/.cargo,/root/.multirust target/kcov target/debug/parity-* && $KCOV_CMD target/debug/parity-* &&
[ $TRAVIS_BRANCH = master ] && [ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] && [ $TRAVIS_PULL_REQUEST = false ] &&
[ $TRAVIS_RUST_VERSION = stable ] && [ $TRAVIS_RUST_VERSION = stable ] &&
@ -53,10 +79,6 @@ after_success: |
pip install --user ghp-import && pip install --user ghp-import &&
/home/travis/.local/bin/ghp-import -n target/doc && /home/travis/.local/bin/ghp-import -n target/doc &&
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
env:
global:
# GH_TOKEN
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
deploy: deploy:
provider: releases provider: releases

View File

@ -70,7 +70,14 @@ pub enum TransactionError {
/// Minimal expected gas price /// Minimal expected gas price
minimal: U256, minimal: U256,
/// Transaction gas price /// Transaction gas price
got: U256 got: U256,
},
/// Sender doesn't have enough funds to pay for this transaction
InsufficientBalance {
/// Senders balance
balance: U256,
/// Transaction cost
cost: U256,
}, },
/// Transaction's gas limit (aka gas) is invalid. /// Transaction's gas limit (aka gas) is invalid.
InvalidGasLimit(OutOfBounds<U256>), InvalidGasLimit(OutOfBounds<U256>),

View File

@ -42,7 +42,7 @@
//! //!
//! let miner: Miner = Miner::default(); //! let miner: Miner = Miner::default();
//! // get status //! // get status
//! assert_eq!(miner.status().transaction_queue_pending, 0); //! assert_eq!(miner.status().transactions_in_pending_queue, 0);
//! //!
//! // Check block for sealing //! // Check block for sealing
//! miner.prepare_sealing(client.deref()); //! miner.prepare_sealing(client.deref());
@ -62,11 +62,11 @@ extern crate rayon;
mod miner; mod miner;
mod transaction_queue; mod transaction_queue;
pub use transaction_queue::TransactionQueue; pub use transaction_queue::{TransactionQueue, AccountDetails};
pub use miner::{Miner}; pub use miner::{Miner};
use std::sync::Mutex; use std::sync::Mutex;
use util::{H256, U256, Address, Bytes}; use util::{H256, Address, Bytes};
use ethcore::client::{BlockChainClient}; use ethcore::client::{BlockChainClient};
use ethcore::block::{ClosedBlock}; use ethcore::block::{ClosedBlock};
use ethcore::error::{Error}; use ethcore::error::{Error};
@ -79,8 +79,8 @@ pub trait MinerService : Send + Sync {
fn status(&self) -> MinerStatus; fn status(&self) -> MinerStatus;
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_nonce: T) -> Result<(), Error> fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) -> Result<(), Error>
where T: Fn(&Address) -> U256; where T: Fn(&Address) -> AccountDetails;
/// Returns hashes of transactions currently in pending /// Returns hashes of transactions currently in pending
fn pending_transactions_hashes(&self) -> Vec<H256>; fn pending_transactions_hashes(&self) -> Vec<H256>;
@ -105,7 +105,9 @@ pub trait MinerService : Send + Sync {
/// Mining status /// Mining status
pub struct MinerStatus { pub struct MinerStatus {
/// Number of transactions in queue with state `pending` (ready to be included in block) /// Number of transactions in queue with state `pending` (ready to be included in block)
pub transaction_queue_pending: usize, pub transactions_in_pending_queue: usize,
/// Number of transactions in queue with state `future` (not yet ready to be included in block) /// Number of transactions in queue with state `future` (not yet ready to be included in block)
pub transaction_queue_future: usize, pub transactions_in_future_queue: usize,
/// Number of transactions included in currently mined block
pub transactions_in_pending_block: usize,
} }

View File

@ -18,14 +18,15 @@ use rayon::prelude::*;
use std::sync::{Mutex, RwLock, Arc}; use std::sync::{Mutex, RwLock, Arc};
use std::sync::atomic; use std::sync::atomic;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::collections::HashSet;
use util::{H256, U256, Address, Bytes, Uint}; use util::{H256, U256, Address, Bytes, Uint};
use ethcore::views::{BlockView}; use ethcore::views::{BlockView};
use ethcore::client::{BlockChainClient, BlockId}; use ethcore::client::{BlockChainClient, BlockId};
use ethcore::block::{ClosedBlock}; use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::error::{Error}; use ethcore::error::{Error};
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use super::{MinerService, MinerStatus, TransactionQueue}; use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails};
/// Keeps track of transactions using priority queue and holds currently mined block. /// Keeps track of transactions using priority queue and holds currently mined block.
pub struct Miner { pub struct Miner {
@ -71,7 +72,7 @@ impl Miner {
/// Get the extra_data that we will seal blocks wuth. /// Get the extra_data that we will seal blocks wuth.
fn gas_floor_target(&self) -> U256 { fn gas_floor_target(&self) -> U256 {
self.gas_floor_target.read().unwrap().clone() *self.gas_floor_target.read().unwrap()
} }
/// Set the author that we will seal blocks as. /// Set the author that we will seal blocks as.
@ -104,16 +105,18 @@ impl MinerService for Miner {
fn status(&self) -> MinerStatus { fn status(&self) -> MinerStatus {
let status = self.transaction_queue.lock().unwrap().status(); let status = self.transaction_queue.lock().unwrap().status();
let block = self.sealing_block.lock().unwrap();
MinerStatus { MinerStatus {
transaction_queue_pending: status.pending, transactions_in_pending_queue: status.pending,
transaction_queue_future: status.future, transactions_in_future_queue: status.future,
transactions_in_pending_block: block.as_ref().map_or(0, |b| b.transactions().len()),
} }
} }
fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_nonce: T) -> Result<(), Error> fn import_transactions<T>(&self, transactions: Vec<SignedTransaction>, fetch_account: T) -> Result<(), Error>
where T: Fn(&Address) -> U256 { where T: Fn(&Address) -> AccountDetails {
let mut transaction_queue = self.transaction_queue.lock().unwrap(); let mut transaction_queue = self.transaction_queue.lock().unwrap();
transaction_queue.add_all(transactions, fetch_nonce) transaction_queue.add_all(transactions, fetch_account)
} }
fn pending_transactions_hashes(&self) -> Vec<H256> { fn pending_transactions_hashes(&self) -> Vec<H256> {
@ -122,10 +125,7 @@ impl MinerService for Miner {
} }
fn prepare_sealing(&self, chain: &BlockChainClient) { fn prepare_sealing(&self, chain: &BlockChainClient) {
let no_of_transactions = 128; let transactions = self.transaction_queue.lock().unwrap().top_transactions();
// TODO: should select transactions orm queue according to gas limit of block.
let transactions = self.transaction_queue.lock().unwrap().top_transactions(no_of_transactions);
let b = chain.prepare_sealing( let b = chain.prepare_sealing(
self.author(), self.author(),
self.gas_floor_target(), self.gas_floor_target(),
@ -182,28 +182,45 @@ impl MinerService for Miner {
let block = BlockView::new(&block); let block = BlockView::new(&block);
block.transactions() block.transactions()
} }
{ {
let in_chain = vec![imported, enacted, invalid];
let in_chain = in_chain
.par_iter()
.flat_map(|h| h.par_iter().map(|h| fetch_transactions(chain, h)));
let out_of_chain = retracted let out_of_chain = retracted
.par_iter() .par_iter()
.map(|h| fetch_transactions(chain, h)); .map(|h| fetch_transactions(chain, h));
in_chain.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));
});
out_of_chain.for_each(|txs| { out_of_chain.for_each(|txs| {
// populate sender // populate sender
for tx in &txs { for tx in &txs {
let _sender = tx.sender(); let _sender = tx.sender();
} }
let mut transaction_queue = self.transaction_queue.lock().unwrap(); let mut transaction_queue = self.transaction_queue.lock().unwrap();
let _ = transaction_queue.add_all(txs, |a| chain.nonce(a)); let _ = transaction_queue.add_all(txs, |a| AccountDetails {
nonce: chain.nonce(a),
balance: chain.balance(a)
});
});
}
// First import all transactions and after that remove old ones
{
let in_chain = {
let mut in_chain = HashSet::new();
in_chain.extend(imported);
in_chain.extend(enacted);
in_chain.extend(invalid);
in_chain
.into_iter()
.collect::<Vec<H256>>()
};
let in_chain = in_chain
.par_iter()
.map(|h: &H256| fetch_transactions(chain, h));
in_chain.for_each(|txs| {
let hashes = txs.iter().map(|tx| tx.hash()).collect::<Vec<H256>>();
let mut transaction_queue = self.transaction_queue.lock().unwrap();
transaction_queue.remove_all(&hashes, |a| AccountDetails {
nonce: chain.nonce(a),
balance: chain.balance(a)
});
}); });
} }

View File

@ -34,7 +34,7 @@
//! use util::crypto::KeyPair; //! use util::crypto::KeyPair;
//! use util::hash::Address; //! use util::hash::Address;
//! use util::numbers::{Uint, U256}; //! use util::numbers::{Uint, U256};
//! use ethminer::TransactionQueue; //! use ethminer::{TransactionQueue, AccountDetails};
//! use ethcore::transaction::*; //! use ethcore::transaction::*;
//! use rustc_serialize::hex::FromHex; //! use rustc_serialize::hex::FromHex;
//! //!
@ -47,16 +47,19 @@
//! //!
//! let st1 = t1.sign(&key.secret()); //! let st1 = t1.sign(&key.secret());
//! let st2 = t2.sign(&key.secret()); //! let st2 = t2.sign(&key.secret());
//! let default_nonce = |_a: &Address| U256::from(10); //! let default_nonce = |_a: &Address| AccountDetails {
//! nonce: U256::from(10),
//! balance: U256::from(1_000_000),
//! };
//! //!
//! let mut txq = TransactionQueue::new(); //! let mut txq = TransactionQueue::new();
//! txq.add(st2.clone(), &default_nonce); //! txq.add(st2.clone(), &default_nonce).unwrap();
//! txq.add(st1.clone(), &default_nonce); //! txq.add(st1.clone(), &default_nonce).unwrap();
//! //!
//! // Check status //! // Check status
//! assert_eq!(txq.status().pending, 2); //! assert_eq!(txq.status().pending, 2);
//! // Check top transactions //! // Check top transactions
//! let top = txq.top_transactions(3); //! let top = txq.top_transactions();
//! assert_eq!(top.len(), 2); //! assert_eq!(top.len(), 2);
//! assert_eq!(top[0], st1); //! assert_eq!(top[0], st1);
//! assert_eq!(top[1], st2); //! assert_eq!(top[1], st2);
@ -66,7 +69,7 @@
//! txq.remove(&st1.hash(), &default_nonce); //! txq.remove(&st1.hash(), &default_nonce);
//! assert_eq!(txq.status().pending, 0); //! assert_eq!(txq.status().pending, 0);
//! assert_eq!(txq.status().future, 1); //! assert_eq!(txq.status().future, 1);
//! assert_eq!(txq.top_transactions(3).len(), 0); //! assert_eq!(txq.top_transactions().len(), 0);
//! } //! }
//! ``` //! ```
//! //!
@ -232,8 +235,6 @@ impl TransactionSet {
} }
} }
// Will be used when rpc merged
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
/// Current status of the queue /// Current status of the queue
pub struct TransactionQueueStatus { pub struct TransactionQueueStatus {
@ -243,6 +244,14 @@ pub struct TransactionQueueStatus {
pub future: usize, pub future: usize,
} }
/// Details of account
pub struct AccountDetails {
/// Most recent account nonce
pub nonce: U256,
/// Current account balance
pub balance: U256,
}
/// TransactionQueue implementation /// TransactionQueue implementation
pub struct TransactionQueue { pub struct TransactionQueue {
/// Gas Price threshold for transactions that can be imported to this queue (defaults to 0) /// Gas Price threshold for transactions that can be imported to this queue (defaults to 0)
@ -308,49 +317,66 @@ impl TransactionQueue {
} }
/// Adds all signed transactions to queue to be verified and imported /// Adds all signed transactions to queue to be verified and imported
pub fn add_all<T>(&mut self, txs: Vec<SignedTransaction>, fetch_nonce: T) -> Result<(), Error> pub fn add_all<T>(&mut self, txs: Vec<SignedTransaction>, fetch_account: T) -> Result<(), Error>
where T: Fn(&Address) -> U256 { where T: Fn(&Address) -> AccountDetails {
for tx in txs.into_iter() { for tx in txs.into_iter() {
try!(self.add(tx, &fetch_nonce)); try!(self.add(tx, &fetch_account));
} }
Ok(()) Ok(())
} }
/// Add signed transaction to queue to be verified and imported /// Add signed transaction to queue to be verified and imported
pub fn add<T>(&mut self, tx: SignedTransaction, fetch_nonce: &T) -> Result<(), Error> pub fn add<T>(&mut self, tx: SignedTransaction, fetch_account: &T) -> Result<(), Error>
where T: Fn(&Address) -> U256 { where T: Fn(&Address) -> AccountDetails {
trace!(target: "miner", "Importing: {:?}", tx.hash());
if tx.gas_price < self.minimal_gas_price { if tx.gas_price < self.minimal_gas_price {
trace!(target: "sync", trace!(target: "miner",
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
tx.hash(), tx.gas_price, self.minimal_gas_price tx.hash(), tx.gas_price, self.minimal_gas_price
); );
return Err(Error::Transaction(TransactionError::InsufficientGasPrice{ return Err(Error::Transaction(TransactionError::InsufficientGasPrice{
minimal: self.minimal_gas_price, minimal: self.minimal_gas_price,
got: tx.gas_price got: tx.gas_price,
})); }));
} }
self.import_tx(try!(VerifiedTransaction::new(tx)), fetch_nonce);
let vtx = try!(VerifiedTransaction::new(tx));
let account = fetch_account(&vtx.sender());
let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas;
if account.balance < cost {
trace!(target: "miner", "Dropping transaction without sufficient balance: {:?} ({} < {})",
vtx.hash(), account.balance, cost);
return Err(Error::Transaction(TransactionError::InsufficientBalance {
cost: cost,
balance: account.balance
}));
}
self.import_tx(vtx, account.nonce);
Ok(()) Ok(())
} }
/// Removes all transactions identified by hashes given in slice /// Removes all transactions identified by hashes given in slice
/// ///
/// If gap is introduced marks subsequent transactions as future /// If gap is introduced marks subsequent transactions as future
pub fn remove_all<T>(&mut self, transaction_hashes: &[H256], fetch_nonce: T) pub fn remove_all<T>(&mut self, transaction_hashes: &[H256], fetch_account: T)
where T: Fn(&Address) -> U256 { where T: Fn(&Address) -> AccountDetails {
for hash in transaction_hashes { for hash in transaction_hashes {
self.remove(&hash, &fetch_nonce); self.remove(&hash, &fetch_account);
} }
} }
/// Removes transaction identified by hashes from queue. /// Removes transaction identified by hashes from queue.
/// ///
/// If gap is introduced marks subsequent transactions as future /// If gap is introduced marks subsequent transactions as future
pub fn remove<T>(&mut self, transaction_hash: &H256, fetch_nonce: &T) pub fn remove<T>(&mut self, transaction_hash: &H256, fetch_account: &T)
where T: Fn(&Address) -> U256 { where T: Fn(&Address) -> AccountDetails {
let transaction = self.by_hash.remove(transaction_hash); let transaction = self.by_hash.remove(transaction_hash);
if transaction.is_none() { if transaction.is_none() {
// We don't know this transaction // We don't know this transaction
@ -360,7 +386,8 @@ impl TransactionQueue {
let transaction = transaction.unwrap(); let transaction = transaction.unwrap();
let sender = transaction.sender(); let sender = transaction.sender();
let nonce = transaction.nonce(); let nonce = transaction.nonce();
let current_nonce = fetch_nonce(&sender); let current_nonce = fetch_account(&sender).nonce;
// Remove from future // Remove from future
let order = self.future.drop(&sender, &nonce); let order = self.future.drop(&sender, &nonce);
@ -401,6 +428,7 @@ impl TransactionQueue {
if k >= current_nonce { if k >= current_nonce {
self.future.insert(*sender, k, order.update_height(k, current_nonce)); self.future.insert(*sender, k, order.update_height(k, current_nonce));
} else { } else {
trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
// Remove the transaction completely // Remove the transaction completely
self.by_hash.remove(&order.hash); self.by_hash.remove(&order.hash);
} }
@ -421,6 +449,7 @@ impl TransactionQueue {
if k >= current_nonce { if k >= current_nonce {
self.future.insert(*sender, k, order.update_height(k, current_nonce)); self.future.insert(*sender, k, order.update_height(k, current_nonce));
} else { } else {
trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce);
self.by_hash.remove(&order.hash); self.by_hash.remove(&order.hash);
} }
} }
@ -430,10 +459,9 @@ impl TransactionQueue {
// Will be used when mining merged // Will be used when mining merged
#[allow(dead_code)] #[allow(dead_code)]
/// Returns top transactions from the queue ordered by priority. /// Returns top transactions from the queue ordered by priority.
pub fn top_transactions(&self, size: usize) -> Vec<SignedTransaction> { pub fn top_transactions(&self) -> Vec<SignedTransaction> {
self.current.by_priority self.current.by_priority
.iter() .iter()
.take(size)
.map(|t| self.by_hash.get(&t.hash).expect("Transaction Queue Inconsistency")) .map(|t| self.by_hash.get(&t.hash).expect("Transaction Queue Inconsistency"))
.map(|t| t.transaction.clone()) .map(|t| t.transaction.clone())
.collect() .collect()
@ -486,19 +514,18 @@ impl TransactionQueue {
/// ///
/// It ignores transactions that has already been imported (same `hash`) and replaces the transaction /// 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. /// iff `(address, nonce)` is the same but `gas_price` is higher.
fn import_tx<T>(&mut self, tx: VerifiedTransaction, fetch_nonce: &T) fn import_tx(&mut self, tx: VerifiedTransaction, state_nonce: U256) {
where T: Fn(&Address) -> U256 {
if self.by_hash.get(&tx.hash()).is_some() { if self.by_hash.get(&tx.hash()).is_some() {
// Transaction is already imported. // Transaction is already imported.
trace!(target: "sync", "Dropping already imported transaction with hash: {:?}", tx.hash()); trace!(target: "miner", "Dropping already imported transaction: {:?}", tx.hash());
return; return;
} }
let address = tx.sender(); let address = tx.sender();
let nonce = tx.nonce(); let nonce = tx.nonce();
let state_nonce = fetch_nonce(&address);
let next_nonce = self.last_nonces let next_nonce = self.last_nonces
.get(&address) .get(&address)
.cloned() .cloned()
@ -512,7 +539,7 @@ impl TransactionQueue {
return; return;
} else if nonce < state_nonce { } else if nonce < state_nonce {
// Droping transaction // Droping transaction
trace!(target: "sync", "Dropping transaction with nonce: {} - expecting: {}", nonce, next_nonce); trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce);
return; return;
} }
@ -521,6 +548,8 @@ impl TransactionQueue {
// But maybe there are some more items waiting in future? // But maybe there are some more items waiting in future?
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce); self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
self.current.enforce_limit(&mut self.by_hash); self.current.enforce_limit(&mut self.by_hash);
trace!(target: "miner", "status: {:?}", self.status());
} }
/// Replaces transaction in given set (could be `future` or `current`). /// Replaces transaction in given set (could be `future` or `current`).
@ -579,8 +608,11 @@ mod test {
new_unsigned_tx(U256::from(123)).sign(&keypair.secret()) new_unsigned_tx(U256::from(123)).sign(&keypair.secret())
} }
fn default_nonce(_address: &Address) -> U256 { fn default_nonce(_address: &Address) -> AccountDetails {
U256::from(123) AccountDetails {
nonce: U256::from(123),
balance: !U256::zero()
}
} }
fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) { fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) {
@ -649,6 +681,25 @@ mod test {
assert_eq!(stats.pending, 1); assert_eq!(stats.pending, 1);
} }
#[test]
fn should_drop_transactions_from_senders_without_balance() {
// given
let mut txq = TransactionQueue::new();
let tx = new_tx();
let account = |a: &Address| AccountDetails {
nonce: default_nonce(a).nonce,
balance: U256::one()
};
// when
txq.add(tx, &account).unwrap_err();
// then
let stats = txq.status();
assert_eq!(stats.pending, 0);
assert_eq!(stats.future, 0);
}
#[test] #[test]
fn should_not_import_transaction_below_min_gas_price_threshold() { fn should_not_import_transaction_below_min_gas_price_threshold() {
// given // given
@ -702,7 +753,7 @@ mod test {
txq.add(tx2.clone(), &default_nonce).unwrap(); txq.add(tx2.clone(), &default_nonce).unwrap();
// then // then
let top = txq.top_transactions(5); let top = txq.top_transactions();
assert_eq!(top[0], tx); assert_eq!(top[0], tx);
assert_eq!(top[1], tx2); assert_eq!(top[1], tx2);
assert_eq!(top.len(), 2); assert_eq!(top.len(), 2);
@ -741,7 +792,7 @@ mod test {
let stats = txq.status(); let stats = txq.status();
assert_eq!(stats.pending, 1); assert_eq!(stats.pending, 1);
assert_eq!(stats.future, 1); assert_eq!(stats.future, 1);
let top = txq.top_transactions(5); let top = txq.top_transactions();
assert_eq!(top.len(), 1); assert_eq!(top.len(), 1);
assert_eq!(top[0], tx); assert_eq!(top[0], tx);
} }
@ -749,8 +800,10 @@ mod test {
#[test] #[test]
fn should_correctly_update_futures_when_removing() { fn should_correctly_update_futures_when_removing() {
// given // given
let prev_nonce = |a: &Address| default_nonce(a) - U256::one(); let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
let next2_nonce = |a: &Address| default_nonce(a) + U256::from(2); !U256::zero() };
let next2_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce + U256::from(2), balance:
!U256::zero() };
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::new();
@ -866,7 +919,7 @@ mod test {
txq.add(tx2.clone(), &default_nonce).unwrap(); txq.add(tx2.clone(), &default_nonce).unwrap();
// then // then
let t = txq.top_transactions(2); let t = txq.top_transactions();
assert_eq!(txq.status().pending, 1); assert_eq!(txq.status().pending, 1);
assert_eq!(t.len(), 1); assert_eq!(t.len(), 1);
assert_eq!(t[0], tx); assert_eq!(t[0], tx);
@ -895,7 +948,7 @@ mod test {
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::new();
let tx = new_tx(); let tx = new_tx();
let last_nonce = tx.nonce + U256::one(); let last_nonce = tx.nonce + U256::one();
let fetch_last_nonce = |_a: &Address| last_nonce; let fetch_last_nonce = |_a: &Address| AccountDetails{ nonce: last_nonce, balance: !U256::zero() };
// when // when
txq.add(tx, &fetch_last_nonce).unwrap(); txq.add(tx, &fetch_last_nonce).unwrap();
@ -909,7 +962,8 @@ mod test {
#[test] #[test]
fn should_not_insert_same_transaction_twice() { fn should_not_insert_same_transaction_twice() {
// given // given
let nonce = |a: &Address| default_nonce(a) + U256::one(); let nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce + U256::one(),
balance: !U256::zero() };
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::new();
let (_tx1, tx2) = new_txs(U256::from(1)); let (_tx1, tx2) = new_txs(U256::from(1));
txq.add(tx2.clone(), &default_nonce).unwrap(); txq.add(tx2.clone(), &default_nonce).unwrap();
@ -949,7 +1003,8 @@ mod test {
#[test] #[test]
fn should_not_move_to_future_if_state_nonce_is_higher() { fn should_not_move_to_future_if_state_nonce_is_higher() {
// given // given
let next_nonce = |a: &Address| default_nonce(a) + U256::one(); let next_nonce = |a: &Address| AccountDetails { nonce: default_nonce(a).nonce + U256::one(), balance:
!U256::zero() };
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::new();
let (tx, tx2) = new_txs(U256::from(1)); let (tx, tx2) = new_txs(U256::from(1));
let tx3 = new_tx(); let tx3 = new_tx();
@ -988,7 +1043,7 @@ mod test {
let stats = txq.status(); let stats = txq.status();
assert_eq!(stats.pending, 1); assert_eq!(stats.pending, 1);
assert_eq!(stats.future, 0); assert_eq!(stats.future, 0);
assert_eq!(txq.top_transactions(1)[0].gas_price, U256::from(200)); assert_eq!(txq.top_transactions()[0].gas_price, U256::from(200));
} }
#[test] #[test]
@ -1018,14 +1073,16 @@ mod test {
let stats = txq.status(); let stats = txq.status();
assert_eq!(stats.future, 0); assert_eq!(stats.future, 0);
assert_eq!(stats.pending, 2); assert_eq!(stats.pending, 2);
assert_eq!(txq.top_transactions(2)[1].gas_price, U256::from(200)); assert_eq!(txq.top_transactions()[1].gas_price, U256::from(200));
} }
#[test] #[test]
fn should_recalculate_height_when_removing_from_future() { fn should_recalculate_height_when_removing_from_future() {
// given // given
let previous_nonce = |a: &Address| default_nonce(a) - U256::one(); let previous_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance:
let next_nonce = |a: &Address| default_nonce(a) + U256::one(); !U256::zero() };
let next_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce + U256::one(), balance:
!U256::zero() };
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::new();
let (tx1, tx2) = new_txs(U256::one()); let (tx1, tx2) = new_txs(U256::one());
txq.add(tx1.clone(), &previous_nonce).unwrap(); txq.add(tx1.clone(), &previous_nonce).unwrap();

View File

@ -19,7 +19,7 @@ use std::collections::HashSet;
use std::sync::{Arc, Weak, Mutex}; use std::sync::{Arc, Weak, Mutex};
use std::ops::Deref; use std::ops::Deref;
use ethsync::{SyncProvider, SyncState}; use ethsync::{SyncProvider, SyncState};
use ethminer::{MinerService}; use ethminer::{MinerService, AccountDetails};
use jsonrpc_core::*; use jsonrpc_core::*;
use util::numbers::*; use util::numbers::*;
use util::sha3::*; use util::sha3::*;
@ -236,7 +236,9 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn block_transaction_count_by_number(&self, params: Params) -> Result<Value, Error> { fn block_transaction_count_by_number(&self, params: Params) -> Result<Value, Error> {
from_params::<(BlockNumber,)>(params) from_params::<(BlockNumber,)>(params)
.and_then(|(block_number,)| match block_number { .and_then(|(block_number,)| match block_number {
BlockNumber::Pending => to_value(&U256::from(take_weak!(self.miner).status().transaction_queue_pending)), BlockNumber::Pending => to_value(
&U256::from(take_weak!(self.miner).status().transactions_in_pending_block)
),
_ => to_value(&take_weak!(self.client).block(block_number.into()) _ => to_value(&take_weak!(self.client).block(block_number.into())
.map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).transactions_count()))) .map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).transactions_count())))
}) })
@ -321,6 +323,15 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
fn work(&self, params: Params) -> Result<Value, Error> { fn work(&self, params: Params) -> Result<Value, Error> {
match params { match params {
Params::None => { Params::None => {
let client = take_weak!(self.client);
// check if we're still syncing and return empty strings int that case
{
let sync = take_weak!(self.sync);
if sync.status().state != SyncState::Idle && client.queue_info().is_empty() {
return to_value(&(String::new(), String::new(), String::new()));
}
}
let miner = take_weak!(self.miner); let miner = take_weak!(self.miner);
let client = take_weak!(self.client); let client = take_weak!(self.client);
let u = miner.sealing_block(client.deref()).lock().unwrap(); let u = miner.sealing_block(client.deref()).lock().unwrap();
@ -331,7 +342,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
let seed_hash = Ethash::get_seedhash(b.block().header().number()); let seed_hash = Ethash::get_seedhash(b.block().header().number());
to_value(&(pow_hash, seed_hash, target)) to_value(&(pow_hash, seed_hash, target))
} }
_ => Err(Error::invalid_params()) _ => Err(Error::internal_error())
} }
}, },
_ => Err(Error::invalid_params()) _ => Err(Error::invalid_params())
@ -370,7 +381,10 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
let signed_transaction = transaction.sign(&secret); let signed_transaction = transaction.sign(&secret);
let hash = signed_transaction.hash(); let hash = signed_transaction.hash();
let import = miner.import_transactions(vec![signed_transaction], |a: &Address| client.nonce(a)); let import = miner.import_transactions(vec![signed_transaction], |a: &Address| AccountDetails {
nonce: client.nonce(a),
balance: client.balance(a),
});
match import { match import {
Ok(_) => to_value(&hash), Ok(_) => to_value(&hash),
Err(e) => { Err(e) => {

View File

@ -43,12 +43,12 @@ fn sync_provider() -> Arc<TestSyncProvider> {
} }
fn miner_service() -> Arc<TestMinerService> { fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService) Arc::new(TestMinerService::default())
} }
struct EthTester { struct EthTester {
client: Arc<TestBlockChainClient>, pub client: Arc<TestBlockChainClient>,
_sync: Arc<TestSyncProvider>, pub sync: Arc<TestSyncProvider>,
_accounts_provider: Arc<TestAccountProvider>, _accounts_provider: Arc<TestAccountProvider>,
_miner: Arc<TestMinerService>, _miner: Arc<TestMinerService>,
hashrates: Arc<RwLock<HashMap<H256, U256>>>, hashrates: Arc<RwLock<HashMap<H256, U256>>>,
@ -68,7 +68,7 @@ impl Default for EthTester {
io.add_delegate(eth); io.add_delegate(eth);
EthTester { EthTester {
client: client, client: client,
_sync: sync, sync: sync,
_accounts_provider: ap, _accounts_provider: ap,
_miner: miner, _miner: miner,
io: io, io: io,
@ -242,6 +242,20 @@ fn rpc_eth_transaction_count_by_number() {
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned())); assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
} }
#[test]
fn rpc_eth_transaction_count_by_number_pending() {
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getBlockTransactionCountByNumber",
"params": ["pending"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x01","id":1}"#;
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
}
#[test] #[test]
fn rpc_eth_uncle_count_by_block_hash() { fn rpc_eth_uncle_count_by_block_hash() {
let request = r#"{ let request = r#"{
@ -346,5 +360,25 @@ fn rpc_eth_compile_serpent() {
assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned())); assert_eq!(EthTester::default().io.handle_request(request), Some(response.to_owned()));
} }
#[test]
fn returns_no_work_if_cant_mine() {
let eth_tester = EthTester::default();
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":["","",""],"id":1}"#;
assert_eq!(eth_tester.io.handle_request(request), Some(response.to_owned()));
}
#[test]
fn returns_error_if_can_mine_and_no_closed_block() {
use ethsync::{SyncState};
let eth_tester = EthTester::default();
eth_tester.sync.status.write().unwrap().state = SyncState::Idle;
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error","data":null},"id":1}"#;
assert_eq!(eth_tester.io.handle_request(request), Some(response.to_owned()));
}

View File

@ -14,23 +14,42 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::{Address, H256, U256, Bytes}; use util::{Address, H256, Bytes};
use util::standard::*; use util::standard::*;
use ethcore::error::Error; use ethcore::error::Error;
use ethcore::client::BlockChainClient; use ethcore::client::BlockChainClient;
use ethcore::block::ClosedBlock; use ethcore::block::ClosedBlock;
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethminer::{MinerService, MinerStatus}; use ethminer::{MinerService, MinerStatus, AccountDetails};
pub struct TestMinerService; pub struct TestMinerService {
pub imported_transactions: RwLock<Vec<H256>>,
pub latest_closed_block: Mutex<Option<ClosedBlock>>,
}
impl Default for TestMinerService {
fn default() -> TestMinerService {
TestMinerService {
imported_transactions: RwLock::new(Vec::new()),
latest_closed_block: Mutex::new(None),
}
}
}
impl MinerService for TestMinerService { impl MinerService for TestMinerService {
/// Returns miner's status. /// Returns miner's status.
fn status(&self) -> MinerStatus { unimplemented!(); } fn status(&self) -> MinerStatus {
MinerStatus {
transactions_in_pending_queue: 0,
transactions_in_future_queue: 0,
transactions_in_pending_block: 1
}
}
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_transactions<T>(&self, _transactions: Vec<SignedTransaction>, _fetch_nonce: T) -> Result<(), Error> where T: Fn(&Address) -> U256 { unimplemented!(); } fn import_transactions<T>(&self, _transactions: Vec<SignedTransaction>, _fetch_account: T) -> Result<(), Error>
where T: Fn(&Address) -> AccountDetails { unimplemented!(); }
/// Returns hashes of transactions currently in pending /// Returns hashes of transactions currently in pending
fn pending_transactions_hashes(&self) -> Vec<H256> { unimplemented!(); } fn pending_transactions_hashes(&self) -> Vec<H256> { unimplemented!(); }
@ -45,7 +64,9 @@ impl MinerService for TestMinerService {
fn prepare_sealing(&self, _chain: &BlockChainClient) { unimplemented!(); } fn prepare_sealing(&self, _chain: &BlockChainClient) { unimplemented!(); }
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock. /// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
fn sealing_block(&self, _chain: &BlockChainClient) -> &Mutex<Option<ClosedBlock>> { unimplemented!(); } fn sealing_block(&self, _chain: &BlockChainClient) -> &Mutex<Option<ClosedBlock>> {
&self.latest_closed_block
}
/// Submit `seal` as a valid solution for the header of `pow_hash`. /// 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. /// Will check the seal, but not actually insert the block into the chain.

View File

@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethsync::{SyncProvider, SyncStatus, SyncState}; use ethsync::{SyncProvider, SyncStatus, SyncState};
use std::sync::{RwLock};
pub struct Config { pub struct Config {
pub protocol_version: u8, pub protocol_version: u8,
@ -22,13 +23,13 @@ pub struct Config {
} }
pub struct TestSyncProvider { pub struct TestSyncProvider {
status: SyncStatus, pub status: RwLock<SyncStatus>,
} }
impl TestSyncProvider { impl TestSyncProvider {
pub fn new(config: Config) -> Self { pub fn new(config: Config) -> Self {
TestSyncProvider { TestSyncProvider {
status: SyncStatus { status: RwLock::new(SyncStatus {
state: SyncState::NotSynced, state: SyncState::NotSynced,
protocol_version: config.protocol_version, protocol_version: config.protocol_version,
start_block_number: 0, start_block_number: 0,
@ -39,14 +40,14 @@ impl TestSyncProvider {
num_peers: config.num_peers, num_peers: config.num_peers,
num_active_peers: 0, num_active_peers: 0,
mem_used: 0, mem_used: 0,
}, }),
} }
} }
} }
impl SyncProvider for TestSyncProvider { impl SyncProvider for TestSyncProvider {
fn status(&self) -> SyncStatus { fn status(&self) -> SyncStatus {
self.status.clone() self.status.read().unwrap().clone()
} }
} }

View File

@ -38,7 +38,7 @@ use range_collection::{RangeCollection, ToUsize, FromUsize};
use ethcore::error::*; use ethcore::error::*;
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethcore::block::Block; use ethcore::block::Block;
use ethminer::{Miner, MinerService}; use ethminer::{Miner, MinerService, AccountDetails};
use io::SyncIo; use io::SyncIo;
use time; use time;
use super::SyncConfig; use super::SyncConfig;
@ -937,6 +937,11 @@ impl ChainSync {
} }
/// Called when peer sends us new transactions /// Called when peer sends us new transactions
fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
// accepting transactions once only fully synced
if !io.is_chain_queue_empty() {
return Ok(());
}
let item_count = r.item_count(); let item_count = r.item_count();
trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count); trace!(target: "sync", "{} -> Transactions ({} entries)", peer_id, item_count);
@ -946,8 +951,11 @@ impl ChainSync {
transactions.push(tx); transactions.push(tx);
} }
let chain = io.chain(); let chain = io.chain();
let fetch_nonce = |a: &Address| chain.nonce(a); let fetch_account = |a: &Address| AccountDetails {
let _ = self.miner.import_transactions(transactions, fetch_nonce); nonce: chain.nonce(a),
balance: chain.balance(a),
};
let _ = self.miner.import_transactions(transactions, fetch_account);
Ok(()) Ok(())
} }
@ -1279,10 +1287,12 @@ impl ChainSync {
/// called when block is imported to chain, updates transactions queue and propagates the blocks /// 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]) {
// Notify miner if io.is_chain_queue_empty() {
self.miner.chain_new_blocks(io.chain(), imported, invalid, enacted, retracted); // Notify miner
// Propagate latests blocks self.miner.chain_new_blocks(io.chain(), imported, invalid, enacted, retracted);
self.propagate_latest_blocks(io); // Propagate latests blocks
self.propagate_latest_blocks(io);
}
// TODO [todr] propagate transactions? // TODO [todr] propagate transactions?
} }
@ -1298,6 +1308,7 @@ mod tests {
use ::SyncConfig; use ::SyncConfig;
use util::*; use util::*;
use super::{PeerInfo, PeerAsking}; use super::{PeerInfo, PeerAsking};
use ethcore::views::BlockView;
use ethcore::header::*; use ethcore::header::*;
use ethcore::client::*; use ethcore::client::*;
use ethminer::{Miner, MinerService}; use ethminer::{Miner, MinerService};
@ -1628,19 +1639,53 @@ mod tests {
let good_blocks = vec![client.block_hash_delta_minus(2)]; let good_blocks = vec![client.block_hash_delta_minus(2)];
let retracted_blocks = vec![client.block_hash_delta_minus(1)]; let retracted_blocks = vec![client.block_hash_delta_minus(1)];
// Add some balance to clients
for h in vec![good_blocks[0], retracted_blocks[0]] {
let block = client.block(BlockId::Hash(h)).unwrap();
let view = BlockView::new(&block);
client.set_balance(view.transactions()[0].sender().unwrap(), U256::from(1_000_000_000));
}
let mut queue = VecDeque::new(); let mut queue = VecDeque::new();
let mut io = TestIo::new(&mut client, &mut queue, None); let mut io = TestIo::new(&mut client, &mut queue, None);
// when // when
sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks); sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks);
assert_eq!(sync.miner.status().transaction_queue_future, 0); assert_eq!(sync.miner.status().transactions_in_future_queue, 0);
assert_eq!(sync.miner.status().transaction_queue_pending, 1); assert_eq!(sync.miner.status().transactions_in_pending_queue, 1);
sync.chain_new_blocks(&mut io, &good_blocks, &[], &[], &retracted_blocks); sync.chain_new_blocks(&mut io, &good_blocks, &[], &[], &retracted_blocks);
// then // then
let status = sync.miner.status(); let status = sync.miner.status();
assert_eq!(status.transaction_queue_pending, 1); assert_eq!(status.transactions_in_pending_queue, 1);
assert_eq!(status.transaction_queue_future, 0); assert_eq!(status.transactions_in_future_queue, 0);
}
#[test]
fn should_not_add_transactions_to_queue_if_not_synced() {
// 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.miner.status().transactions_in_future_queue, 0);
assert_eq!(sync.miner.status().transactions_in_pending_queue, 0);
sync.chain_new_blocks(&mut io, &good_blocks, &[], &[], &retracted_blocks);
// then
let status = sync.miner.status();
assert_eq!(status.transactions_in_pending_queue, 0);
assert_eq!(status.transactions_in_future_queue, 0);
} }
#[test] #[test]

View File

@ -37,6 +37,10 @@ pub trait SyncIo {
fn peer_info(&self, peer_id: PeerId) -> String { fn peer_info(&self, peer_id: PeerId) -> String {
peer_id.to_string() peer_id.to_string()
} }
/// Returns if the chain block queue empty
fn is_chain_queue_empty(&self) -> bool {
self.chain().queue_info().is_empty()
}
} }
/// Wraps `NetworkContext` and the blockchain client /// Wraps `NetworkContext` and the blockchain client

View File

@ -173,7 +173,7 @@ impl NetworkProtocolHandler<SyncMessage> for EthSync {
SyncMessage::NewChainHead => { SyncMessage::NewChainHead => {
let mut sync_io = NetSyncIo::new(io, self.chain.deref()); let mut sync_io = NetSyncIo::new(io, self.chain.deref());
self.sync.write().unwrap().chain_new_head(&mut sync_io); self.sync.write().unwrap().chain_new_head(&mut sync_io);
} },
_ => {/* Ignore other messages */}, _ => {/* Ignore other messages */},
} }
} }

View File

@ -29,7 +29,6 @@ sha3 = { path = "sha3" }
serde = "0.7.0" serde = "0.7.0"
clippy = { version = "0.0.50", optional = true } clippy = { version = "0.0.50", optional = true }
json-tests = { path = "json-tests" } json-tests = { path = "json-tests" }
rustc_version = "0.1.0"
igd = "0.4.2" igd = "0.4.2"
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }
libc = "0.2.7" libc = "0.2.7"
@ -44,3 +43,4 @@ dev = ["clippy"]
[build-dependencies] [build-dependencies]
vergen = "*" vergen = "*"
rustc_version = "0.1.0"

View File

@ -15,9 +15,23 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate vergen; extern crate vergen;
extern crate rustc_version;
use vergen::*; use vergen::*;
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
fn main() { fn main() {
vergen(OutputFns::all()).unwrap(); vergen(OutputFns::all()).unwrap();
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("rustc_version.rs");
let mut f = File::create(&dest_path).unwrap();
f.write_all(format!("
/// Returns compiler version.
pub fn rustc_version() -> &'static str {{
\"{}\"
}}
", rustc_version::version()).as_bytes()).unwrap();
} }

View File

@ -109,9 +109,7 @@ extern crate log as rlog;
extern crate igd; extern crate igd;
extern crate ethcore_devtools as devtools; extern crate ethcore_devtools as devtools;
extern crate libc; extern crate libc;
extern crate rustc_version;
extern crate target_info; extern crate target_info;
extern crate vergen;
extern crate bigint; extern crate bigint;
extern crate chrono; extern crate chrono;

View File

@ -20,9 +20,9 @@ use std::fs::File;
use common::*; use common::*;
use rlp::{Stream, RlpStream}; use rlp::{Stream, RlpStream};
use target_info::Target; use target_info::Target;
use rustc_version;
include!(concat!(env!("OUT_DIR"), "/version.rs")); include!(concat!(env!("OUT_DIR"), "/version.rs"));
include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
#[derive(Debug,Clone,PartialEq,Eq)] #[derive(Debug,Clone,PartialEq,Eq)]
/// Diff type for specifying a change (or not). /// Diff type for specifying a change (or not).
@ -70,7 +70,13 @@ pub fn contents(name: &str) -> Result<Bytes, UtilError> {
/// Get the standard version string for this software. /// Get the standard version string for this software.
pub fn version() -> String { pub fn version() -> String {
format!("Parity/v{}-unstable-{}-{}/{}-{}-{}/rustc{}", env!("CARGO_PKG_VERSION"), short_sha(), commit_date().replace("-", ""), Target::arch(), Target::os(), Target::env(), rustc_version::version()) let sha3 = short_sha();
let sha3_dash = if sha3.is_empty() { "" } else { "-" };
let commit_date = commit_date().replace("-", "");
let date_dash = if commit_date.is_empty() { "" } else { "-" };
let env = Target::env();
let env_dash = if env.is_empty() { "" } else { "-" };
format!("Parity/v{}-unstable{}{}{}{}/{}-{}{}{}/rustc{}", env!("CARGO_PKG_VERSION"), sha3_dash, sha3, date_dash, commit_date, Target::arch(), Target::os(), env_dash, env, rustc_version())
} }
/// Get the standard version data for this software. /// Get the standard version data for this software.
@ -82,7 +88,7 @@ pub fn version_data() -> Bytes {
u32::from_str(env!("CARGO_PKG_VERSION_PATCH")).unwrap(); u32::from_str(env!("CARGO_PKG_VERSION_PATCH")).unwrap();
s.append(&v); s.append(&v);
s.append(&"Parity"); s.append(&"Parity");
s.append(&format!("{}", rustc_version::version())); s.append(&format!("{}", rustc_version()));
s.append(&&Target::os()[0..2]); s.append(&&Target::os()[0..2]);
s.out() s.out()
} }