diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs
index 3a77efe85..c375e91dc 100644
--- a/ethcore/src/miner/miner.rs
+++ b/ethcore/src/miner/miner.rs
@@ -15,7 +15,7 @@
// along with Parity. If not, see .
use std::time::{Instant, Duration};
-use std::collections::{BTreeMap, HashSet, HashMap};
+use std::collections::{BTreeMap, BTreeSet, HashSet, HashMap};
use std::sync::Arc;
use ansi_term::Colour;
@@ -838,7 +838,40 @@ impl miner::MinerService for Miner {
self.transaction_queue.all_transactions()
}
- fn ready_transactions(&self, chain: &C) -> Vec> where
+ fn pending_transaction_hashes(&self, chain: &C) -> BTreeSet where
+ C: ChainInfo + Sync,
+ {
+ let chain_info = chain.chain_info();
+
+ let from_queue = || self.transaction_queue.pending_hashes(
+ |sender| self.nonce_cache.read().get(sender).cloned(),
+ );
+
+ let from_pending = || {
+ self.map_existing_pending_block(|sealing| {
+ sealing.transactions()
+ .iter()
+ .map(|signed| signed.hash())
+ .collect()
+ }, chain_info.best_block_number)
+ };
+
+ match self.options.pending_set {
+ PendingSet::AlwaysQueue => {
+ from_queue()
+ },
+ PendingSet::AlwaysSealing => {
+ from_pending().unwrap_or_default()
+ },
+ PendingSet::SealingOrElseQueue => {
+ from_pending().unwrap_or_else(from_queue)
+ },
+ }
+ }
+
+ fn ready_transactions(&self, chain: &C)
+ -> Vec>
+ where
C: ChainInfo + Nonce + Sync,
{
let chain_info = chain.chain_info();
@@ -1043,8 +1076,12 @@ impl miner::MinerService for Miner {
// 2. We ignore blocks that are `invalid` because it doesn't have any meaning in terms of the transactions that
// are in those blocks
- // Clear nonce cache
- self.nonce_cache.write().clear();
+ let has_new_best_block = enacted.len() > 0;
+
+ if has_new_best_block {
+ // Clear nonce cache
+ self.nonce_cache.write().clear();
+ }
// First update gas limit in transaction queue and minimal gas price.
let gas_limit = *chain.best_block_header().gas_limit();
@@ -1069,10 +1106,12 @@ impl miner::MinerService for Miner {
});
}
- // ...and at the end remove the old ones
- self.transaction_queue.cull(client);
+ if has_new_best_block {
+ // ...and at the end remove the old ones
+ self.transaction_queue.cull(client);
+ }
- if enacted.len() > 0 || (imported.len() > 0 && self.options.reseal_on_uncle) {
+ if has_new_best_block || (imported.len() > 0 && self.options.reseal_on_uncle) {
// Reset `next_allowed_reseal` in case a block is imported.
// Even if min_period is high, we will always attempt to create
// new pending block.
diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs
index fc18e5d47..340285b9b 100644
--- a/ethcore/src/miner/mod.rs
+++ b/ethcore/src/miner/mod.rs
@@ -28,7 +28,7 @@ pub mod stratum;
pub use self::miner::{Miner, MinerOptions, Penalization, PendingSet, AuthoringParams};
use std::sync::Arc;
-use std::collections::BTreeMap;
+use std::collections::{BTreeSet, BTreeMap};
use bytes::Bytes;
use ethereum_types::{H256, U256, Address};
@@ -164,7 +164,13 @@ pub trait MinerService : Send + Sync {
fn next_nonce(&self, chain: &C, address: &Address) -> U256
where C: Nonce + Sync;
- /// Get a list of all ready transactions.
+ /// Get a set of all pending transaction hashes.
+ ///
+ /// Depending on the settings may look in transaction pool or only in pending block.
+ fn pending_transaction_hashes(&self, chain: &C) -> BTreeSet where
+ C: ChainInfo + Sync;
+
+ /// Get a list of all ready transactions either ordered by priority or unordered (cheaper).
///
/// Depending on the settings may look in transaction pool or only in pending block.
fn ready_transactions(&self, chain: &C) -> Vec>
diff --git a/miner/src/pool/queue.rs b/miner/src/pool/queue.rs
index 96f3e4ef7..bc8abbf22 100644
--- a/miner/src/pool/queue.rs
+++ b/miner/src/pool/queue.rs
@@ -19,7 +19,7 @@
use std::{cmp, fmt};
use std::sync::Arc;
use std::sync::atomic::{self, AtomicUsize};
-use std::collections::{BTreeMap, HashMap};
+use std::collections::{BTreeMap, BTreeSet, HashMap};
use ethereum_types::{H256, U256, Address};
use parking_lot::RwLock;
@@ -285,7 +285,20 @@ impl TransactionQueue {
self.pool.read().pending(ready).collect()
}
- /// Returns current pneding transactions.
+ /// Computes unordered set of pending hashes.
+ ///
+ /// Since strict nonce-checking is not required, you may get some false positive future transactions as well.
+ pub fn pending_hashes(
+ &self,
+ nonce: N,
+ ) -> BTreeSet where
+ N: Fn(&Address) -> Option,
+ {
+ let ready = ready::OptionalState::new(nonce);
+ self.pool.read().pending(ready).map(|tx| tx.hash).collect()
+ }
+
+ /// Returns current pending transactions ordered by priority.
///
/// NOTE: This may return a cached version of pending transaction set.
/// Re-computing the pending set is possible with `#collect_pending` method,
diff --git a/miner/src/pool/ready.rs b/miner/src/pool/ready.rs
index c2829b34a..a8e4dd6f8 100644
--- a/miner/src/pool/ready.rs
+++ b/miner/src/pool/ready.rs
@@ -130,6 +130,43 @@ impl txpool::Ready for Condition {
}
}
+/// Readiness checker that only relies on nonce cache (does actually go to state).
+///
+/// Checks readiness of transactions by comparing the nonce to state nonce. If nonce
+/// isn't found in provided state nonce store, defaults to the tx nonce and updates
+/// the nonce store. Useful for using with a state nonce cache when false positives are allowed.
+pub struct OptionalState {
+ nonces: HashMap,
+ state: C,
+}
+
+impl OptionalState {
+ pub fn new(state: C) -> Self {
+ OptionalState {
+ nonces: Default::default(),
+ state,
+ }
+ }
+}
+
+impl Option> txpool::Ready for OptionalState {
+ fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness {
+ let sender = tx.sender();
+ let state = &self.state;
+ let nonce = self.nonces.entry(*sender).or_insert_with(|| {
+ state(sender).unwrap_or_else(|| tx.transaction.nonce)
+ });
+ match tx.transaction.nonce.cmp(nonce) {
+ cmp::Ordering::Greater => txpool::Readiness::Future,
+ cmp::Ordering::Less => txpool::Readiness::Stale,
+ cmp::Ordering::Equal => {
+ *nonce = *nonce + 1.into();
+ txpool::Readiness::Ready
+ },
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs
index c8d9e414b..867215173 100644
--- a/rpc/src/v1/helpers/mod.rs
+++ b/rpc/src/v1/helpers/mod.rs
@@ -40,7 +40,7 @@ mod subscription_manager;
pub use self::dispatch::{Dispatcher, FullDispatcher};
pub use self::network_settings::NetworkSettings;
pub use self::poll_manager::PollManager;
-pub use self::poll_filter::{PollFilter, limit_logs};
+pub use self::poll_filter::{PollFilter, SyncPollFilter, limit_logs};
pub use self::requests::{
TransactionRequest, FilledTransactionRequest, ConfirmationRequest, ConfirmationPayload, CallRequest,
};
diff --git a/rpc/src/v1/helpers/poll_filter.rs b/rpc/src/v1/helpers/poll_filter.rs
index 614370783..4fdd6100e 100644
--- a/rpc/src/v1/helpers/poll_filter.rs
+++ b/rpc/src/v1/helpers/poll_filter.rs
@@ -1,18 +1,40 @@
//! Helper type with all filter state data.
-use std::collections::HashSet;
+use std::{
+ collections::{BTreeSet, HashSet},
+ sync::Arc,
+};
use ethereum_types::H256;
+use parking_lot::Mutex;
use v1::types::{Filter, Log};
pub type BlockNumber = u64;
+/// Thread-safe filter state.
+#[derive(Clone)]
+pub struct SyncPollFilter(Arc>);
+
+impl SyncPollFilter {
+ /// New `SyncPollFilter`
+ pub fn new(f: PollFilter) -> Self {
+ SyncPollFilter(Arc::new(Mutex::new(f)))
+ }
+
+ /// Modify underlying filter
+ pub fn modify(&self, f: F) -> R where
+ F: FnOnce(&mut PollFilter) -> R,
+ {
+ f(&mut self.0.lock())
+ }
+}
+
/// Filter state.
#[derive(Clone)]
pub enum PollFilter {
/// Number of last block which client was notified about.
Block(BlockNumber),
- /// Hashes of all transactions which client was notified about.
- PendingTransaction(Vec),
+ /// Hashes of all pending transactions the client knows about.
+ PendingTransaction(BTreeSet),
/// Number of From block number, last seen block hash, pending logs and log filter itself.
Logs(BlockNumber, Option, HashSet, Filter)
}
diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs
index d6434bba8..975c944c7 100644
--- a/rpc/src/v1/impls/eth.rs
+++ b/rpc/src/v1/impls/eth.rs
@@ -608,11 +608,9 @@ impl Eth for EthClient<
}
fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture