diff --git a/.travis.yml b/.travis.yml index 91c50fa13..5d6de65c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,20 +8,43 @@ branches: - /^stable-.*$/ - /^beta$/ - /^stable$/ +git: + depth: 3 matrix: - fast_finish: false + fast_finish: true allow_failures: - rust: nightly include: - 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: apt: true directories: - - target/debug/deps - - target/debug/build - - target/release/deps - - target/release/build + - $TRAVIS_BUILD_DIR/target - $HOME/.cargo addons: apt: @@ -29,22 +52,25 @@ addons: - libcurl4-openssl-dev - libelf-dev - libdw-dev + script: -- cargo build --release --verbose ${FEATURES} -- cargo test --release --verbose ${FEATURES} ${TARGETS} -#- cargo bench --no-run ${FEATURES} ${TARGETS} -- tar cvzf parity${ARCHIVE_SUFFIX}.tar.gz -C target/release parity + - if [ "$RUN_TESTS" = "true" ]; then cargo test --release --verbose ${FEATURES} ${TARGETS}; fi + - if [ "$RUN_BENCHES" = "true" ]; then cargo bench --no-run ${FEATURES} ${TARGETS}; fi + - if [ "$RUN_BUILD" = "true" ]; then cargo build --release --verbose ${FEATURES}; fi + - if [ "$RUN_BUILD" = "true" ]; then tar cvzf parity${ARCHIVE_SUFFIX}.tar.gz -C target/release parity; fi + after_success: | + [ "$RUN_COVERAGE" = "true" ] && 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 ../.. && 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-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-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-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-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-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-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/deps/ethcore_util-* && + $KCOV_CMD target/debug/deps/ethash-* && + $KCOV_CMD target/debug/deps/ethcore-* && + $KCOV_CMD target/debug/deps/ethsync-* && + $KCOV_CMD target/debug/deps/ethcore_rpc-* && + $KCOV_CMD target/debug/deps/ethminer-* && + $KCOV_CMD target/debug/parity-* && [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && [ $TRAVIS_RUST_VERSION = stable ] && @@ -53,10 +79,6 @@ after_success: | pip install --user ghp-import && /home/travis/.local/bin/ghp-import -n target/doc && 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: provider: releases diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 72127c754..482a8de01 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -70,7 +70,14 @@ pub enum TransactionError { /// Minimal expected gas price minimal: U256, /// 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. InvalidGasLimit(OutOfBounds), diff --git a/miner/src/lib.rs b/miner/src/lib.rs index a431bd44e..648e9b3b5 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -42,7 +42,7 @@ //! //! let miner: Miner = Miner::default(); //! // get status -//! assert_eq!(miner.status().transaction_queue_pending, 0); +//! assert_eq!(miner.status().transactions_in_pending_queue, 0); //! //! // Check block for sealing //! miner.prepare_sealing(client.deref()); @@ -62,11 +62,11 @@ extern crate rayon; mod miner; mod transaction_queue; -pub use transaction_queue::TransactionQueue; +pub use transaction_queue::{TransactionQueue, AccountDetails}; pub use miner::{Miner}; use std::sync::Mutex; -use util::{H256, U256, Address, Bytes}; +use util::{H256, Address, Bytes}; use ethcore::client::{BlockChainClient}; use ethcore::block::{ClosedBlock}; use ethcore::error::{Error}; @@ -79,8 +79,8 @@ pub trait MinerService : Send + Sync { fn status(&self) -> MinerStatus; /// Imports transactions to transaction queue. - fn import_transactions(&self, transactions: Vec, fetch_nonce: T) -> Result<(), Error> - where T: Fn(&Address) -> U256; + fn import_transactions(&self, transactions: Vec, fetch_account: T) -> Result<(), Error> + where T: Fn(&Address) -> AccountDetails; /// Returns hashes of transactions currently in pending fn pending_transactions_hashes(&self) -> Vec; @@ -105,7 +105,9 @@ pub trait MinerService : Send + Sync { /// Mining status pub struct MinerStatus { /// 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) - 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, } diff --git a/miner/src/miner.rs b/miner/src/miner.rs index 6d5b3086e..832dc5d02 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -18,14 +18,15 @@ use rayon::prelude::*; use std::sync::{Mutex, RwLock, Arc}; use std::sync::atomic; use std::sync::atomic::AtomicBool; +use std::collections::HashSet; use util::{H256, U256, Address, Bytes, Uint}; use ethcore::views::{BlockView}; use ethcore::client::{BlockChainClient, BlockId}; -use ethcore::block::{ClosedBlock}; +use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::error::{Error}; 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. pub struct Miner { @@ -71,7 +72,7 @@ impl Miner { /// Get the extra_data that we will seal blocks wuth. 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. @@ -104,16 +105,18 @@ impl MinerService for Miner { fn status(&self) -> MinerStatus { let status = self.transaction_queue.lock().unwrap().status(); + let block = self.sealing_block.lock().unwrap(); MinerStatus { - transaction_queue_pending: status.pending, - transaction_queue_future: status.future, + transactions_in_pending_queue: status.pending, + transactions_in_future_queue: status.future, + transactions_in_pending_block: block.as_ref().map_or(0, |b| b.transactions().len()), } } - fn import_transactions(&self, transactions: Vec, fetch_nonce: T) -> Result<(), Error> - where T: Fn(&Address) -> U256 { + fn import_transactions(&self, transactions: Vec, fetch_account: T) -> Result<(), Error> + where T: Fn(&Address) -> AccountDetails { 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 { @@ -174,28 +177,45 @@ impl MinerService for Miner { let block = BlockView::new(&block); 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 .par_iter() .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::>(); - transaction_queue.remove_all(&hashes, |a| chain.nonce(a)); - }); out_of_chain.for_each(|txs| { // populate sender for tx in &txs { let _sender = tx.sender(); } let mut transaction_queue = self.transaction_queue.lock().unwrap(); - let _ = transaction_queue.add_all(txs, |a| chain.nonce(a)); + 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::>() + }; + + 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::>(); + let mut transaction_queue = self.transaction_queue.lock().unwrap(); + transaction_queue.remove_all(&hashes, |a| AccountDetails { + nonce: chain.nonce(a), + balance: chain.balance(a) + }); }); } diff --git a/miner/src/transaction_queue.rs b/miner/src/transaction_queue.rs index 324a46364..a1fc20b0f 100644 --- a/miner/src/transaction_queue.rs +++ b/miner/src/transaction_queue.rs @@ -34,7 +34,7 @@ //! use util::crypto::KeyPair; //! use util::hash::Address; //! use util::numbers::{Uint, U256}; -//! use ethminer::TransactionQueue; +//! use ethminer::{TransactionQueue, AccountDetails}; //! use ethcore::transaction::*; //! use rustc_serialize::hex::FromHex; //! @@ -47,11 +47,14 @@ //! //! let st1 = t1.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(); -//! txq.add(st2.clone(), &default_nonce); -//! txq.add(st1.clone(), &default_nonce); +//! txq.add(st2.clone(), &default_nonce).unwrap(); +//! txq.add(st1.clone(), &default_nonce).unwrap(); //! //! // Check status //! assert_eq!(txq.status().pending, 2); @@ -232,8 +235,6 @@ impl TransactionSet { } } -// Will be used when rpc merged -#[allow(dead_code)] #[derive(Debug)] /// Current status of the queue pub struct TransactionQueueStatus { @@ -243,6 +244,14 @@ pub struct TransactionQueueStatus { pub future: usize, } +/// Details of account +pub struct AccountDetails { + /// Most recent account nonce + pub nonce: U256, + /// Current account balance + pub balance: U256, +} + /// TransactionQueue implementation pub struct TransactionQueue { /// 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 - pub fn add_all(&mut self, txs: Vec, fetch_nonce: T) -> Result<(), Error> - where T: Fn(&Address) -> U256 { + pub fn add_all(&mut self, txs: Vec, fetch_account: T) -> Result<(), Error> + where T: Fn(&Address) -> AccountDetails { for tx in txs.into_iter() { - try!(self.add(tx, &fetch_nonce)); + try!(self.add(tx, &fetch_account)); } Ok(()) } /// Add signed transaction to queue to be verified and imported - pub fn add(&mut self, tx: SignedTransaction, fetch_nonce: &T) -> Result<(), Error> - where T: Fn(&Address) -> U256 { + pub fn add(&mut self, tx: SignedTransaction, fetch_account: &T) -> Result<(), Error> + where T: Fn(&Address) -> AccountDetails { + + trace!(target: "miner", "Importing: {:?}", tx.hash()); if tx.gas_price < self.minimal_gas_price { - trace!(target: "sync", + trace!(target: "miner", "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", tx.hash(), tx.gas_price, self.minimal_gas_price ); return Err(Error::Transaction(TransactionError::InsufficientGasPrice{ 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(()) } /// Removes all transactions identified by hashes given in slice /// /// If gap is introduced marks subsequent transactions as future - pub fn remove_all(&mut self, transaction_hashes: &[H256], fetch_nonce: T) - where T: Fn(&Address) -> U256 { + pub fn remove_all(&mut self, transaction_hashes: &[H256], fetch_account: T) + where T: Fn(&Address) -> AccountDetails { for hash in transaction_hashes { - self.remove(&hash, &fetch_nonce); + self.remove(&hash, &fetch_account); } } /// Removes transaction identified by hashes from queue. /// /// If gap is introduced marks subsequent transactions as future - pub fn remove(&mut self, transaction_hash: &H256, fetch_nonce: &T) - where T: Fn(&Address) -> U256 { + pub fn remove(&mut self, transaction_hash: &H256, fetch_account: &T) + where T: Fn(&Address) -> AccountDetails { + let transaction = self.by_hash.remove(transaction_hash); if transaction.is_none() { // We don't know this transaction @@ -360,7 +386,8 @@ impl TransactionQueue { let transaction = transaction.unwrap(); let sender = transaction.sender(); let nonce = transaction.nonce(); - let current_nonce = fetch_nonce(&sender); + let current_nonce = fetch_account(&sender).nonce; + // Remove from future let order = self.future.drop(&sender, &nonce); @@ -401,6 +428,7 @@ impl TransactionQueue { if k >= current_nonce { self.future.insert(*sender, k, order.update_height(k, current_nonce)); } else { + trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); // Remove the transaction completely self.by_hash.remove(&order.hash); } @@ -421,6 +449,7 @@ impl TransactionQueue { if k >= current_nonce { self.future.insert(*sender, k, order.update_height(k, current_nonce)); } else { + trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); self.by_hash.remove(&order.hash); } } @@ -486,19 +515,18 @@ impl TransactionQueue { /// /// It ignores transactions that has already been imported (same `hash`) and replaces the transaction /// iff `(address, nonce)` is the same but `gas_price` is higher. - fn import_tx(&mut self, tx: VerifiedTransaction, fetch_nonce: &T) - where T: Fn(&Address) -> U256 { + fn import_tx(&mut self, tx: VerifiedTransaction, state_nonce: U256) { if self.by_hash.get(&tx.hash()).is_some() { // 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; } + let address = tx.sender(); let nonce = tx.nonce(); - let state_nonce = fetch_nonce(&address); let next_nonce = self.last_nonces .get(&address) .cloned() @@ -512,7 +540,7 @@ impl TransactionQueue { return; } else if nonce < state_nonce { // 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; } @@ -521,6 +549,8 @@ impl TransactionQueue { // But maybe there are some more items waiting in future? self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce); self.current.enforce_limit(&mut self.by_hash); + + trace!(target: "miner", "status: {:?}", self.status()); } /// Replaces transaction in given set (could be `future` or `current`). @@ -579,8 +609,11 @@ mod test { new_unsigned_tx(U256::from(123)).sign(&keypair.secret()) } - fn default_nonce(_address: &Address) -> U256 { - U256::from(123) + fn default_nonce(_address: &Address) -> AccountDetails { + AccountDetails { + nonce: U256::from(123), + balance: !U256::zero() + } } fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) { @@ -649,6 +682,25 @@ mod test { 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] fn should_not_import_transaction_below_min_gas_price_threshold() { // given @@ -749,8 +801,10 @@ mod test { #[test] fn should_correctly_update_futures_when_removing() { // given - let prev_nonce = |a: &Address| default_nonce(a) - U256::one(); - let next2_nonce = |a: &Address| default_nonce(a) + U256::from(2); + let prev_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance: + !U256::zero() }; + let next2_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce + U256::from(2), balance: + !U256::zero() }; let mut txq = TransactionQueue::new(); @@ -895,7 +949,7 @@ mod test { let mut txq = TransactionQueue::new(); let tx = new_tx(); 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 txq.add(tx, &fetch_last_nonce).unwrap(); @@ -909,7 +963,8 @@ mod test { #[test] fn should_not_insert_same_transaction_twice() { // 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 (_tx1, tx2) = new_txs(U256::from(1)); txq.add(tx2.clone(), &default_nonce).unwrap(); @@ -949,7 +1004,8 @@ mod test { #[test] fn should_not_move_to_future_if_state_nonce_is_higher() { // 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 (tx, tx2) = new_txs(U256::from(1)); let tx3 = new_tx(); @@ -1024,8 +1080,10 @@ mod test { #[test] fn should_recalculate_height_when_removing_from_future() { // given - let previous_nonce = |a: &Address| default_nonce(a) - U256::one(); - let next_nonce = |a: &Address| default_nonce(a) + U256::one(); + let previous_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce - U256::one(), balance: + !U256::zero() }; + let next_nonce = |a: &Address| AccountDetails{ nonce: default_nonce(a).nonce + U256::one(), balance: + !U256::zero() }; let mut txq = TransactionQueue::new(); let (tx1, tx2) = new_txs(U256::one()); txq.add(tx1.clone(), &previous_nonce).unwrap(); diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 520b37045..7ecfb86de 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -19,7 +19,7 @@ use std::collections::HashSet; use std::sync::{Arc, Weak, Mutex}; use std::ops::Deref; use ethsync::{SyncProvider, SyncState}; -use ethminer::{MinerService}; +use ethminer::{MinerService, AccountDetails}; use jsonrpc_core::*; use util::numbers::*; use util::sha3::*; @@ -236,7 +236,9 @@ impl Eth for EthClient fn block_transaction_count_by_number(&self, params: Params) -> Result { from_params::<(BlockNumber,)>(params) .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()) .map_or_else(U256::zero, |bytes| U256::from(BlockView::new(&bytes).transactions_count()))) }) @@ -379,7 +381,10 @@ impl Eth for EthClient let signed_transaction = transaction.sign(&secret); 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 { Ok(_) => to_value(&hash), Err(e) => { diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 280557e4b..389de103b 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -242,6 +242,20 @@ fn rpc_eth_transaction_count_by_number() { 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] fn rpc_eth_uncle_count_by_block_hash() { let request = r#"{ diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 3a60bd136..44b94ac14 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::{Address, H256, U256, Bytes}; +use util::{Address, H256, Bytes}; use util::standard::*; use ethcore::error::Error; use ethcore::client::BlockChainClient; use ethcore::block::ClosedBlock; use ethcore::transaction::SignedTransaction; -use ethminer::{MinerService, MinerStatus}; +use ethminer::{MinerService, MinerStatus, AccountDetails}; pub struct TestMinerService { pub imported_transactions: RwLock>, @@ -39,10 +39,17 @@ impl Default for TestMinerService { impl MinerService for TestMinerService { /// 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. - fn import_transactions(&self, _transactions: Vec, _fetch_nonce: T) -> Result<(), Error> where T: Fn(&Address) -> U256 { unimplemented!(); } + fn import_transactions(&self, _transactions: Vec, _fetch_account: T) -> Result<(), Error> + where T: Fn(&Address) -> AccountDetails { unimplemented!(); } /// Returns hashes of transactions currently in pending fn pending_transactions_hashes(&self) -> Vec { unimplemented!(); } diff --git a/sync/src/chain.rs b/sync/src/chain.rs index def9ad7f4..c94e89445 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -38,7 +38,7 @@ use range_collection::{RangeCollection, ToUsize, FromUsize}; use ethcore::error::*; use ethcore::transaction::SignedTransaction; use ethcore::block::Block; -use ethminer::{Miner, MinerService}; +use ethminer::{Miner, MinerService, AccountDetails}; use io::SyncIo; use time; use super::SyncConfig; @@ -961,8 +961,11 @@ impl ChainSync { transactions.push(tx); } let chain = io.chain(); - let fetch_nonce = |a: &Address| chain.nonce(a); - let _ = self.miner.import_transactions(transactions, fetch_nonce); + let fetch_account = |a: &Address| AccountDetails { + nonce: chain.nonce(a), + balance: chain.balance(a), + }; + let _ = self.miner.import_transactions(transactions, fetch_account); Ok(()) } @@ -1317,6 +1320,7 @@ mod tests { use ::SyncConfig; use util::*; use super::{PeerInfo, PeerAsking}; + use ethcore::views::BlockView; use ethcore::header::*; use ethcore::client::*; use ethminer::{Miner, MinerService}; @@ -1647,19 +1651,27 @@ mod tests { let good_blocks = vec![client.block_hash_delta_minus(2)]; 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 io = TestIo::new(&mut client, &mut queue, None); // when sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, true); - assert_eq!(sync.miner.status().transaction_queue_future, 0); - assert_eq!(sync.miner.status().transaction_queue_pending, 1); + assert_eq!(sync.miner.status().transactions_in_future_queue, 0); + assert_eq!(sync.miner.status().transactions_in_pending_queue, 1); sync.chain_new_blocks(&mut io, &good_blocks, &[], &[], &retracted_blocks, true); + // then let status = sync.miner.status(); - assert_eq!(status.transaction_queue_pending, 1); - assert_eq!(status.transaction_queue_future, 0); + assert_eq!(status.transactions_in_pending_queue, 1); + assert_eq!(status.transactions_in_future_queue, 0); } #[test] @@ -1679,14 +1691,14 @@ mod tests { // when sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, false); - assert_eq!(sync.miner.status().transaction_queue_future, 0); - assert_eq!(sync.miner.status().transaction_queue_pending, 0); + 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, false); // then let status = sync.miner.status(); - assert_eq!(status.transaction_queue_pending, 0); - assert_eq!(status.transaction_queue_future, 0); + assert_eq!(status.transactions_in_pending_queue, 0); + assert_eq!(status.transactions_in_future_queue, 0); } #[test]