diff --git a/miner/src/pool/queue.rs b/miner/src/pool/queue.rs index 4ebdf9e3f1f..0ba917c6f15 100644 --- a/miner/src/pool/queue.rs +++ b/miner/src/pool/queue.rs @@ -177,7 +177,8 @@ impl TransactionQueue { let _timer = ::trace_time::PerfTimer::new("pool::verify_and_import"); let options = self.options.read().clone(); - let verifier = verifier::Verifier::new(client, options, self.insertion_id.clone()); + let min_effective_gas_price = self.pool.read().minimal_entry_score().map(scoring::bump_gas_price); + let verifier = verifier::Verifier::new(client, options, self.insertion_id.clone(), min_effective_gas_price); let results = transactions .into_par_iter() .map(|transaction| verifier.verify_transaction(transaction)) diff --git a/miner/src/pool/scoring.rs b/miner/src/pool/scoring.rs index e7551ed6a3d..5a898eeaaea 100644 --- a/miner/src/pool/scoring.rs +++ b/miner/src/pool/scoring.rs @@ -37,6 +37,12 @@ use super::{PrioritizationStrategy, VerifiedTransaction}; /// `new_gas_price > old_gas_price + old_gas_price >> SHIFT` const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25% +/// Calculate minimal gas price requirement. +#[inline] +pub fn bump_gas_price(old_gp: U256) -> U256 { + old_gp.saturating_add(old_gp >> GAS_PRICE_BUMP_SHIFT) +} + /// Simple, gas-price based scoring for transactions. /// /// NOTE: Currently penalization does not apply to new transactions that enter the pool. @@ -60,7 +66,7 @@ impl txpool::Scoring for NonceAndGasPrice { let old_gp = old.transaction.gas_price; let new_gp = new.transaction.gas_price; - let min_required_gp = old_gp + (old_gp >> GAS_PRICE_BUMP_SHIFT); + let min_required_gp = bump_gas_price(old_gp); match min_required_gp.cmp(&new_gp) { cmp::Ordering::Greater => txpool::scoring::Choice::RejectNew, diff --git a/miner/src/pool/tests/mod.rs b/miner/src/pool/tests/mod.rs index 552903a4bb2..e28057d39f8 100644 --- a/miner/src/pool/tests/mod.rs +++ b/miner/src/pool/tests/mod.rs @@ -796,3 +796,10 @@ fn should_include_local_transaction_to_a_full_pool() { // then assert_eq!(txq.status().status.transaction_count, 1); } + + +#[test] +fn should_reject_early_in_case_gas_price_is_less_than_min_effective() { + // TODO [ToDr] + assert_eq!(true, false); +} diff --git a/miner/src/pool/verifier.rs b/miner/src/pool/verifier.rs index 4675303928d..0560982c7f8 100644 --- a/miner/src/pool/verifier.rs +++ b/miner/src/pool/verifier.rs @@ -129,15 +129,22 @@ impl Transaction { pub struct Verifier { client: C, options: Options, + min_effective_gas_price: Option, id: Arc, } impl Verifier { /// Creates new transaction verfier with specified options. - pub fn new(client: C, options: Options, id: Arc) -> Self { + pub fn new( + client: C, + options: Options, + id: Arc, + min_effective_gas_price: Option, + ) -> Self { Verifier { client, options, + min_effective_gas_price, id, } } @@ -190,22 +197,40 @@ impl txpool::Verifier for Verifier { } let is_own = tx.is_local(); - // Quick exit for non-service transactions - if tx.gas_price() < &self.options.minimal_gas_price - && !tx.gas_price().is_zero() - && !is_own - { - trace!( - target: "txqueue", - "[{:?}] Rejected tx below minimal gas price threshold: {} < {}", - hash, - tx.gas_price(), - self.options.minimal_gas_price, - ); - bail!(transaction::Error::InsufficientGasPrice { - minimal: self.options.minimal_gas_price, - got: *tx.gas_price(), - }); + // Quick exit for non-service and non-local transactions + // + // We're checking if the transaction is below configured minimal gas price + // or the effective minimal gas price in case the pool is full. + if !tx.gas_price().is_zero() && !is_own { + if tx.gas_price() < &self.options.minimal_gas_price { + trace!( + target: "txqueue", + "[{:?}] Rejected tx below minimal gas price threshold: {} < {}", + hash, + tx.gas_price(), + self.options.minimal_gas_price, + ); + bail!(transaction::Error::InsufficientGasPrice { + minimal: self.options.minimal_gas_price, + got: *tx.gas_price(), + }); + } + + if let Some(ref gp) = self.min_effective_gas_price { + if tx.gas_price() < gp { + trace!( + target: "txqueue", + "[{:?}] Rejected tx below minimal effective gas price threshold: {} < {}", + hash, + tx.gas_price(), + gp, + ); + bail!(transaction::Error::InsufficientGasPrice { + minimal: *gp, + got: *tx.gas_price(), + }); + } + } } // Some more heavy checks below. diff --git a/transaction-pool/src/pool.rs b/transaction-pool/src/pool.rs index dcd52a3e7e6..84e256ed33e 100644 --- a/transaction-pool/src/pool.rs +++ b/transaction-pool/src/pool.rs @@ -392,6 +392,26 @@ impl Pool where self.worst_transactions.iter().next().map(|x| x.transaction.transaction.clone()) } + /// Returns the score of the worst transaction if the pool is almost full. + /// + /// This method can be used to determine what is the minimal required score + /// for the replacement transaction. If `None` is returned it means that + /// there is still plenty of room in the pool. Otherwise we return + /// `Some` with the score of the worst transaction. + pub fn minimal_entry_score(&self) -> Option { + let threshold = |x: usize| x * 9 / 10; + let is_full = { + self.by_hash.len() > threshold(self.options.max_count) + || self.mem_usage > threshold(self.options.max_mem_usage) + }; + + if !is_full { + return None + } + + self.worst_transactions.iter().next().map(|x| x.score.clone()) + } + /// Returns an iterator of pending (ready) transactions. pub fn pending>(&self, ready: R) -> PendingIterator { PendingIterator {