From 45b4fae70d7e82c92ec990c81e3614d3b7b34e56 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 28 Mar 2019 13:23:59 +0000 Subject: [PATCH] Extract `should_replace` from Scoring and supply pooled txs from same sender (#116) * Extract ShouldReplace trait and pass to import * Pass old sender txs into should_replace * Pass sender txs in with both old and new tx * Docs and extract replace to separate file * Fix tests and docs * Bump version (2.0) * Copyright 2019 * Borrow Transaction * Make ShouldReplace immutable * Tabs * Fix indentation * Fix tests compilation --- transaction-pool/Cargo.toml | 2 +- transaction-pool/src/lib.rs | 2 + transaction-pool/src/pool.rs | 39 ++++-- transaction-pool/src/replace.rs | 53 +++++++ transaction-pool/src/scoring.rs | 7 - transaction-pool/src/tests/helpers.rs | 14 +- transaction-pool/src/tests/mod.rs | 193 +++++++++++++------------- 7 files changed, 189 insertions(+), 121 deletions(-) create mode 100644 transaction-pool/src/replace.rs diff --git a/transaction-pool/Cargo.toml b/transaction-pool/Cargo.toml index 6d7053a..cb022f4 100644 --- a/transaction-pool/Cargo.toml +++ b/transaction-pool/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Generic transaction pool." name = "transaction-pool" -version = "1.13.3" +version = "2.0.0" license = "GPL-3.0" authors = ["Parity Technologies "] repository = "https://github.com/paritytech/parity-common" diff --git a/transaction-pool/src/lib.rs b/transaction-pool/src/lib.rs index fc18c06..def45fe 100644 --- a/transaction-pool/src/lib.rs +++ b/transaction-pool/src/lib.rs @@ -85,6 +85,7 @@ mod listener; mod options; mod pool; mod ready; +mod replace; mod status; mod transactions; mod verifier; @@ -96,6 +97,7 @@ pub use self::listener::{Listener, NoopListener}; pub use self::options::Options; pub use self::pool::{Pool, PendingIterator, UnorderedIterator, Transaction}; pub use self::ready::{Ready, Readiness}; +pub use self::replace::{ShouldReplace, ReplaceTransaction}; pub use self::scoring::Scoring; pub use self::status::{LightStatus, Status}; pub use self::verifier::Verifier; diff --git a/transaction-pool/src/pool.rs b/transaction-pool/src/pool.rs index 55f2685..e653684 100644 --- a/transaction-pool/src/pool.rs +++ b/transaction-pool/src/pool.rs @@ -22,6 +22,7 @@ use error; use listener::{Listener, NoopListener}; use options::Options; use ready::{Ready, Readiness}; +use replace::{ShouldReplace, ReplaceTransaction}; use scoring::{self, Scoring, ScoreWithRef}; use status::{LightStatus, Status}; use transactions::{AddResult, Transactions}; @@ -130,10 +131,12 @@ impl Pool where /// NOTE: The transaction may push out some other transactions from the pool /// either because of limits (see `Options`) or because `Scoring` decides that the transaction /// replaces an existing transaction from that sender. - /// If any limit is reached the transaction with the lowest `Score` is evicted to make room. + /// + /// If any limit is reached the transaction with the lowest `Score` will be compared with the + /// new transaction via the supplied `ShouldReplace` implementation and may be evicted. /// /// The `Listener` will be informed on any drops or rejections. - pub fn import(&mut self, transaction: T) -> error::Result, T::Hash> { + pub fn import(&mut self, transaction: T, replace: &ShouldReplace) -> error::Result, T::Hash> { let mem_usage = transaction.mem_usage(); if self.by_hash.contains_key(transaction.hash()) { @@ -150,7 +153,7 @@ impl Pool where // Avoid using should_replace, but rather use scoring for that. { let remove_worst = |s: &mut Self, transaction| { - match s.remove_worst(transaction) { + match s.remove_worst(transaction, replace) { Err(err) => { s.listener.rejected(transaction, &err); Err(err) @@ -283,22 +286,32 @@ impl Pool where /// /// Returns `None` in case we couldn't decide if the transaction should replace the worst transaction or not. /// In such case we will accept the transaction even though it is going to exceed the limit. - fn remove_worst(&mut self, transaction: &Transaction) -> error::Result>, T::Hash> { + fn remove_worst(&mut self, transaction: &Transaction, replace: &ShouldReplace) -> error::Result>, T::Hash> { let to_remove = match self.worst_transactions.iter().next_back() { // No elements to remove? and the pool is still full? None => { warn!("The pool is full but there are no transactions to remove."); return Err(error::Error::TooCheapToEnter(transaction.hash().clone(), "unknown".into())) }, - Some(old) => match self.scoring.should_replace(&old.transaction, transaction) { - // We can't decide which of them should be removed, so accept both. - scoring::Choice::InsertNew => None, - // New transaction is better than the worst one so we can replace it. - scoring::Choice::ReplaceOld => Some(old.clone()), - // otherwise fail - scoring::Choice::RejectNew => { - return Err(error::Error::TooCheapToEnter(transaction.hash().clone(), format!("{:#x}", old.score))) - }, + Some(old) => { + let txs = &self.transactions; + let get_replace_tx = |tx| { + let sender_txs = txs.get(transaction.sender()).map(|txs| txs.iter().as_slice()); + ReplaceTransaction::new(tx, sender_txs) + }; + let old_replace = get_replace_tx(&old.transaction); + let new_replace = get_replace_tx(transaction); + + match replace.should_replace(&old_replace, &new_replace) { + // We can't decide which of them should be removed, so accept both. + scoring::Choice::InsertNew => None, + // New transaction is better than the worst one so we can replace it. + scoring::Choice::ReplaceOld => Some(old.clone()), + // otherwise fail + scoring::Choice::RejectNew => { + return Err(error::Error::TooCheapToEnter(transaction.hash().clone(), format!("{:#x}", old.score))) + }, + } }, }; diff --git a/transaction-pool/src/replace.rs b/transaction-pool/src/replace.rs new file mode 100644 index 0000000..ba905b5 --- /dev/null +++ b/transaction-pool/src/replace.rs @@ -0,0 +1,53 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! When queue limits are reached, decide whether to replace an existing transaction from the pool + +use pool::Transaction; +use scoring::Choice; + +/// Encapsulates a transaction to be compared, along with pooled transactions from the same sender +pub struct ReplaceTransaction<'a, T> { + /// The transaction to be compared for replacement + pub transaction: &'a Transaction, + /// Other transactions currently in the pool for the same sender + pub pooled_by_sender: Option<&'a [Transaction]>, +} + +impl<'a, T> ReplaceTransaction<'a, T> { + /// Creates a new `ReplaceTransaction` + pub fn new(transaction: &'a Transaction, pooled_by_sender: Option<&'a [Transaction]>) -> Self { + ReplaceTransaction { + transaction, + pooled_by_sender, + } + } +} + +impl<'a, T> ::std::ops::Deref for ReplaceTransaction<'a, T> { + type Target = Transaction; + fn deref(&self) -> &Self::Target { + &self.transaction + } +} + +/// Chooses whether a new transaction should replace an existing transaction if the pool is full. +pub trait ShouldReplace { + /// Decides if `new` should push out `old` transaction from the pool. + /// + /// NOTE returning `InsertNew` here can lead to some transactions being accepted above pool limits. + fn should_replace(&self, old: &ReplaceTransaction, new: &ReplaceTransaction) -> Choice; +} diff --git a/transaction-pool/src/scoring.rs b/transaction-pool/src/scoring.rs index 78b78e6..8ae57c8 100644 --- a/transaction-pool/src/scoring.rs +++ b/transaction-pool/src/scoring.rs @@ -72,14 +72,12 @@ pub enum Change { /// - Returned `Score`s should match ordering of `compare` method. /// - `compare` will be called only within a context of transactions from the same sender. /// - `choose` may be called even if `compare` returns `Ordering::Equal` -/// - `should_replace` is used to decide if new transaction should push out an old transaction already in the queue. /// - `Score`s and `compare` should align with `Ready` implementation. /// /// Example: Natural ordering of Ethereum transactions. /// - `compare`: compares transaction `nonce` () /// - `choose`: compares transactions `gasPrice` (decides if old transaction should be replaced) /// - `update_scores`: score defined as `gasPrice` if `n==0` and `max(scores[n-1], gasPrice)` if `n>0` -/// - `should_replace`: compares `gasPrice` (decides if transaction from a different sender is more valuable) /// pub trait Scoring: fmt::Debug { /// A score of a transaction. @@ -98,11 +96,6 @@ pub trait Scoring: fmt::Debug { /// (i.e. score at index `i` represents transaction at the same index) fn update_scores(&self, txs: &[Transaction], scores: &mut [Self::Score], change: Change); - /// Decides if `new` should push out `old` transaction from the pool. - /// - /// NOTE returning `InsertNew` here can lead to some transactions being accepted above pool limits. - fn should_replace(&self, old: &T, new: &T) -> Choice; - /// Decides if the transaction should ignore per-sender limit in the pool. /// /// If you return `true` for given transaction it's going to be accepted even though diff --git a/transaction-pool/src/tests/helpers.rs b/transaction-pool/src/tests/helpers.rs index bd09638..c4510a8 100644 --- a/transaction-pool/src/tests/helpers.rs +++ b/transaction-pool/src/tests/helpers.rs @@ -18,7 +18,7 @@ use std::cmp; use std::collections::HashMap; use ethereum_types::{H160 as Sender, U256}; -use {pool, scoring, Scoring, Ready, Readiness}; +use {pool, scoring, Scoring, ShouldReplace, ReplaceTransaction, Ready, Readiness}; use super::Transaction; #[derive(Debug, Default)] @@ -68,7 +68,13 @@ impl Scoring for DummyScoring { } } - fn should_replace(&self, old: &Transaction, new: &Transaction) -> scoring::Choice { + fn should_ignore_sender_limit(&self, _new: &Transaction) -> bool { + self.always_insert + } +} + +impl ShouldReplace for DummyScoring { + fn should_replace(&self, old: &ReplaceTransaction, new: &ReplaceTransaction) -> scoring::Choice { if self.always_insert { scoring::Choice::InsertNew } else if new.gas_price > old.gas_price { @@ -77,10 +83,6 @@ impl Scoring for DummyScoring { scoring::Choice::RejectNew } } - - fn should_ignore_sender_limit(&self, _new: &Transaction) -> bool { - self.always_insert - } } #[derive(Default)] diff --git a/transaction-pool/src/tests/mod.rs b/transaction-pool/src/tests/mod.rs index a3adcaa..fc83580 100644 --- a/transaction-pool/src/tests/mod.rs +++ b/transaction-pool/src/tests/mod.rs @@ -57,6 +57,11 @@ impl TestPool { } } +fn import, L: Listener>(txq: &mut Pool, tx: Transaction) + -> Result, Error<::Hash>> { + txq.import(tx, &mut DummyScoring::default()) +} + #[test] fn should_clear_queue() { // given @@ -71,8 +76,8 @@ fn should_clear_queue() { let tx2 = b.tx().nonce(1).mem_usage(1).new(); // add - txq.import(tx1).unwrap(); - txq.import(tx2).unwrap(); + import(&mut txq, tx1).unwrap(); + import(&mut txq, tx2).unwrap(); assert_eq!(txq.light_status(), LightStatus { mem_usage: 1, transaction_count: 2, @@ -99,8 +104,8 @@ fn should_not_allow_same_transaction_twice() { let tx2 = b.tx().nonce(0).new(); // when - txq.import(tx1).unwrap(); - txq.import(tx2).unwrap_err(); + import(&mut txq, tx1).unwrap(); + import(&mut txq, tx2).unwrap_err(); // then assert_eq!(txq.light_status().transaction_count, 1); @@ -115,8 +120,8 @@ fn should_replace_transaction() { let tx2 = b.tx().nonce(0).gas_price(2).new(); // when - txq.import(tx1).unwrap(); - txq.import(tx2).unwrap(); + import(&mut txq, tx1).unwrap(); + import(&mut txq, tx2).unwrap(); // then assert_eq!(txq.light_status().transaction_count, 1); @@ -134,8 +139,8 @@ fn should_reject_if_above_count() { let tx1 = b.tx().nonce(0).new(); let tx2 = b.tx().nonce(1).new(); let hash = tx2.hash.clone(); - txq.import(tx1).unwrap(); - assert_eq!(txq.import(tx2).unwrap_err(), error::Error::TooCheapToEnter(hash, "0x0".into())); + import(&mut txq, tx1).unwrap(); + assert_eq!(import(&mut txq, tx2).unwrap_err(), error::Error::TooCheapToEnter(hash, "0x0".into())); assert_eq!(txq.light_status().transaction_count, 1); txq.clear(); @@ -143,8 +148,8 @@ fn should_reject_if_above_count() { // Replace first let tx1 = b.tx().nonce(0).new(); let tx2 = b.tx().nonce(0).sender(1).gas_price(2).new(); - txq.import(tx1).unwrap(); - txq.import(tx2).unwrap(); + import(&mut txq, tx1).unwrap(); + import(&mut txq, tx2).unwrap(); assert_eq!(txq.light_status().transaction_count, 1); } @@ -160,8 +165,8 @@ fn should_reject_if_above_mem_usage() { let tx1 = b.tx().nonce(1).mem_usage(1).new(); let tx2 = b.tx().nonce(2).mem_usage(2).new(); let hash = tx2.hash.clone(); - txq.import(tx1).unwrap(); - assert_eq!(txq.import(tx2).unwrap_err(), error::Error::TooCheapToEnter(hash, "0x0".into())); + import(&mut txq, tx1).unwrap(); + assert_eq!(import(&mut txq, tx2).unwrap_err(), error::Error::TooCheapToEnter(hash, "0x0".into())); assert_eq!(txq.light_status().transaction_count, 1); txq.clear(); @@ -169,8 +174,8 @@ fn should_reject_if_above_mem_usage() { // Replace first let tx1 = b.tx().nonce(1).mem_usage(1).new(); let tx2 = b.tx().nonce(1).sender(1).gas_price(2).mem_usage(1).new(); - txq.import(tx1).unwrap(); - txq.import(tx2).unwrap(); + import(&mut txq, tx1).unwrap(); + import(&mut txq, tx2).unwrap(); assert_eq!(txq.light_status().transaction_count, 1); } @@ -186,8 +191,8 @@ fn should_reject_if_above_sender_count() { let tx1 = b.tx().nonce(1).new(); let tx2 = b.tx().nonce(2).new(); let hash = tx2.hash.clone(); - txq.import(tx1).unwrap(); - assert_eq!(txq.import(tx2).unwrap_err(), error::Error::TooCheapToEnter(hash, "0x0".into())); + import(&mut txq, tx1).unwrap(); + assert_eq!(import(&mut txq, tx2).unwrap_err(), error::Error::TooCheapToEnter(hash, "0x0".into())); assert_eq!(txq.light_status().transaction_count, 1); txq.clear(); @@ -196,9 +201,9 @@ fn should_reject_if_above_sender_count() { let tx1 = b.tx().nonce(1).new(); let tx2 = b.tx().nonce(2).gas_price(2).new(); let hash = tx2.hash.clone(); - txq.import(tx1).unwrap(); + import(&mut txq, tx1).unwrap(); // This results in error because we also compare nonces - assert_eq!(txq.import(tx2).unwrap_err(), error::Error::TooCheapToEnter(hash, "0x0".into())); + assert_eq!(import(&mut txq, tx2).unwrap_err(), error::Error::TooCheapToEnter(hash, "0x0".into())); assert_eq!(txq.light_status().transaction_count, 1); } @@ -208,25 +213,25 @@ fn should_construct_pending() { let b = TransactionBuilder::default(); let mut txq = TestPool::default(); - let tx0 = txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); - let tx1 = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap(); + let tx0 = import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); + let tx1 = import(&mut txq, b.tx().nonce(1).gas_price(5).new()).unwrap(); - let tx9 = txq.import(b.tx().sender(2).nonce(0).new()).unwrap(); + let tx9 = import(&mut txq, b.tx().sender(2).nonce(0).new()).unwrap(); - let tx5 = txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); - let tx6 = txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); - let tx7 = txq.import(b.tx().sender(1).nonce(2).new()).unwrap(); - let tx8 = txq.import(b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); + let tx5 = import(&mut txq, b.tx().sender(1).nonce(0).new()).unwrap(); + let tx6 = import(&mut txq, b.tx().sender(1).nonce(1).new()).unwrap(); + let tx7 = import(&mut txq, b.tx().sender(1).nonce(2).new()).unwrap(); + let tx8 = import(&mut txq, b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); - let tx2 = txq.import(b.tx().nonce(2).new()).unwrap(); + let tx2 = import(&mut txq, b.tx().nonce(2).new()).unwrap(); // this transaction doesn't get to the block despite high gas price // because of block gas limit and simplistic ordering algorithm. - txq.import(b.tx().nonce(3).gas_price(4).new()).unwrap(); + import(&mut txq, b.tx().nonce(3).gas_price(4).new()).unwrap(); //gap - txq.import(b.tx().nonce(5).new()).unwrap(); + import(&mut txq, b.tx().nonce(5).new()).unwrap(); // gap - txq.import(b.tx().sender(1).nonce(5).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(5).new()).unwrap(); assert_eq!(txq.light_status().transaction_count, 11); assert_eq!(txq.status(NonceReady::default()), Status { @@ -268,21 +273,21 @@ fn should_return_unordered_iterator() { let b = TransactionBuilder::default(); let mut txq = TestPool::default(); - let tx0 = txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); - let tx1 = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap(); - let tx2 = txq.import(b.tx().nonce(2).new()).unwrap(); - let tx3 = txq.import(b.tx().nonce(3).gas_price(4).new()).unwrap(); + let tx0 = import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); + let tx1 = import(&mut txq, b.tx().nonce(1).gas_price(5).new()).unwrap(); + let tx2 = import(&mut txq, b.tx().nonce(2).new()).unwrap(); + let tx3 = import(&mut txq, b.tx().nonce(3).gas_price(4).new()).unwrap(); //gap - txq.import(b.tx().nonce(5).new()).unwrap(); + import(&mut txq, b.tx().nonce(5).new()).unwrap(); - let tx5 = txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); - let tx6 = txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); - let tx7 = txq.import(b.tx().sender(1).nonce(2).new()).unwrap(); - let tx8 = txq.import(b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); + let tx5 = import(&mut txq, b.tx().sender(1).nonce(0).new()).unwrap(); + let tx6 = import(&mut txq, b.tx().sender(1).nonce(1).new()).unwrap(); + let tx7 = import(&mut txq, b.tx().sender(1).nonce(2).new()).unwrap(); + let tx8 = import(&mut txq, b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); // gap - txq.import(b.tx().sender(1).nonce(5).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(5).new()).unwrap(); - let tx9 = txq.import(b.tx().sender(2).nonce(0).new()).unwrap(); + let tx9 = import(&mut txq, b.tx().sender(2).nonce(0).new()).unwrap(); assert_eq!(txq.light_status().transaction_count, 11); assert_eq!(txq.status(NonceReady::default()), Status { stalled: 0, @@ -328,24 +333,24 @@ fn should_update_scoring_correctly() { let b = TransactionBuilder::default(); let mut txq = TestPool::default(); - let tx9 = txq.import(b.tx().sender(2).nonce(0).new()).unwrap(); + let tx9 = import(&mut txq, b.tx().sender(2).nonce(0).new()).unwrap(); - let tx5 = txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); - let tx6 = txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); - let tx7 = txq.import(b.tx().sender(1).nonce(2).new()).unwrap(); - let tx8 = txq.import(b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); + let tx5 = import(&mut txq, b.tx().sender(1).nonce(0).new()).unwrap(); + let tx6 = import(&mut txq, b.tx().sender(1).nonce(1).new()).unwrap(); + let tx7 = import(&mut txq, b.tx().sender(1).nonce(2).new()).unwrap(); + let tx8 = import(&mut txq, b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap(); - let tx0 = txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); - let tx1 = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap(); - let tx2 = txq.import(b.tx().nonce(2).new()).unwrap(); + let tx0 = import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); + let tx1 = import(&mut txq, b.tx().nonce(1).gas_price(5).new()).unwrap(); + let tx2 = import(&mut txq, b.tx().nonce(2).new()).unwrap(); // this transaction doesn't get to the block despite high gas price // because of block gas limit and simplistic ordering algorithm. - txq.import(b.tx().nonce(3).gas_price(4).new()).unwrap(); + import(&mut txq, b.tx().nonce(3).gas_price(4).new()).unwrap(); //gap - txq.import(b.tx().nonce(5).new()).unwrap(); + import(&mut txq, b.tx().nonce(5).new()).unwrap(); // gap - txq.import(b.tx().sender(1).nonce(5).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(5).new()).unwrap(); assert_eq!(txq.light_status().transaction_count, 11); assert_eq!(txq.status(NonceReady::default()), Status { @@ -390,9 +395,9 @@ fn should_remove_transaction() { let b = TransactionBuilder::default(); let mut txq = TestPool::default(); - let tx1 = txq.import(b.tx().nonce(0).new()).unwrap(); - let tx2 = txq.import(b.tx().nonce(1).new()).unwrap(); - txq.import(b.tx().nonce(2).new()).unwrap(); + let tx1 = import(&mut txq, b.tx().nonce(0).new()).unwrap(); + let tx2 = import(&mut txq, b.tx().nonce(1).new()).unwrap(); + import(&mut txq, b.tx().nonce(2).new()).unwrap(); assert_eq!(txq.light_status().transaction_count, 3); // when @@ -411,13 +416,13 @@ fn should_cull_stalled_transactions() { let b = TransactionBuilder::default(); let mut txq = TestPool::default(); - txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); - txq.import(b.tx().nonce(1).new()).unwrap(); - txq.import(b.tx().nonce(3).new()).unwrap(); + import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); + import(&mut txq, b.tx().nonce(1).new()).unwrap(); + import(&mut txq, b.tx().nonce(3).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(5).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(0).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(1).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(5).new()).unwrap(); assert_eq!(txq.status(NonceReady::new(1)), Status { stalled: 2, @@ -447,12 +452,12 @@ fn should_cull_stalled_transactions_from_a_sender() { let b = TransactionBuilder::default(); let mut txq = TestPool::default(); - txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); - txq.import(b.tx().nonce(1).new()).unwrap(); + import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); + import(&mut txq, b.tx().nonce(1).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(2).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(0).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(1).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(2).new()).unwrap(); assert_eq!(txq.status(NonceReady::new(2)), Status { stalled: 4, @@ -483,10 +488,10 @@ fn should_re_insert_after_cull() { let b = TransactionBuilder::default(); let mut txq = TestPool::default(); - txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); - txq.import(b.tx().nonce(1).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(1).new()).unwrap(); + import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); + import(&mut txq, b.tx().nonce(1).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(0).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(1).new()).unwrap(); assert_eq!(txq.status(NonceReady::new(1)), Status { stalled: 2, pending: 2, @@ -500,8 +505,8 @@ fn should_re_insert_after_cull() { pending: 2, future: 0, }); - txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(0).new()).unwrap(); + import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(0).new()).unwrap(); assert_eq!(txq.status(NonceReady::new(1)), Status { stalled: 2, @@ -518,8 +523,8 @@ fn should_return_worst_transaction() { assert!(txq.worst_transaction().is_none()); // when - txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); - txq.import(b.tx().sender(1).nonce(0).gas_price(4).new()).unwrap(); + import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(0).gas_price(4).new()).unwrap(); // then assert_eq!(txq.worst_transaction().unwrap().gas_price, 4.into()); @@ -533,10 +538,10 @@ fn should_return_is_full() { assert!(!txq.is_full()); // when - txq.import(b.tx().nonce(0).gas_price(110).new()).unwrap(); + import(&mut txq, b.tx().nonce(0).gas_price(110).new()).unwrap(); assert!(!txq.is_full()); - txq.import(b.tx().sender(1).nonce(0).gas_price(100).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(0).gas_price(100).new()).unwrap(); // then assert!(txq.is_full()); @@ -550,7 +555,7 @@ fn should_import_even_if_limit_is_reached_and_should_replace_returns_insert_new( max_count: 1, ..Default::default() }); - txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + txq.import(b.tx().nonce(0).gas_price(5).new(), &mut DummyScoring::always_insert()).unwrap(); assert_eq!(txq.light_status(), LightStatus { transaction_count: 1, senders: 1, @@ -558,7 +563,7 @@ fn should_import_even_if_limit_is_reached_and_should_replace_returns_insert_new( }); // when - txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap(); + txq.import(b.tx().nonce(1).gas_price(5).new(), &mut DummyScoring::always_insert()).unwrap(); // then assert_eq!(txq.light_status(), LightStatus { @@ -576,7 +581,7 @@ fn should_not_import_even_if_limit_is_reached_and_should_replace_returns_false() max_count: 1, ..Default::default() }); - txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + import(&mut txq, b.tx().nonce(0).gas_price(5).new()).unwrap(); assert_eq!(txq.light_status(), LightStatus { transaction_count: 1, senders: 1, @@ -584,7 +589,7 @@ fn should_not_import_even_if_limit_is_reached_and_should_replace_returns_false() }); // when - let err = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap_err(); + let err = import(&mut txq, b.tx().nonce(1).gas_price(5).new()).unwrap_err(); // then assert_eq!(err, error::Error::TooCheapToEnter("0x00000000000000000000000000000000000000000000000000000000000001f5".into(), "0x5".into())); @@ -604,7 +609,7 @@ fn should_import_even_if_sender_limit_is_reached() { max_per_sender: 1, ..Default::default() }); - txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap(); + txq.import(b.tx().nonce(0).gas_price(5).new(), &mut DummyScoring::always_insert()).unwrap(); assert_eq!(txq.light_status(), LightStatus { transaction_count: 1, senders: 1, @@ -612,7 +617,7 @@ fn should_import_even_if_sender_limit_is_reached() { }); // when - txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap(); + txq.import(b.tx().nonce(1).gas_price(5).new(), &mut DummyScoring::always_insert()).unwrap(); // then assert_eq!(txq.light_status(), LightStatus { @@ -671,26 +676,26 @@ mod listener { assert!(results.borrow().is_empty()); // Regular import - txq.import(b.tx().nonce(1).new()).unwrap(); + import(&mut txq, b.tx().nonce(1).new()).unwrap(); assert_eq!(*results.borrow(), &["added"]); // Already present (no notification) - txq.import(b.tx().nonce(1).new()).unwrap_err(); + import(&mut txq, b.tx().nonce(1).new()).unwrap_err(); assert_eq!(*results.borrow(), &["added"]); // Push out the first one - txq.import(b.tx().nonce(1).gas_price(1).new()).unwrap(); + import(&mut txq, b.tx().nonce(1).gas_price(1).new()).unwrap(); assert_eq!(*results.borrow(), &["added", "replaced"]); // Reject - txq.import(b.tx().nonce(1).new()).unwrap_err(); + import(&mut txq, b.tx().nonce(1).new()).unwrap_err(); assert_eq!(*results.borrow(), &["added", "replaced", "rejected"]); results.borrow_mut().clear(); // Different sender (accept) - txq.import(b.tx().sender(1).nonce(1).gas_price(2).new()).unwrap(); + import(&mut txq, b.tx().sender(1).nonce(1).gas_price(2).new()).unwrap(); assert_eq!(*results.borrow(), &["added"]); // Third sender push out low gas price - txq.import(b.tx().sender(2).nonce(1).gas_price(4).new()).unwrap(); + import(&mut txq, b.tx().sender(2).nonce(1).gas_price(4).new()).unwrap(); assert_eq!(*results.borrow(), &["added", "dropped", "added"]); // Reject (too cheap) - txq.import(b.tx().sender(2).nonce(1).gas_price(2).new()).unwrap_err(); + import(&mut txq, b.tx().sender(2).nonce(1).gas_price(2).new()).unwrap_err(); assert_eq!(*results.borrow(), &["added", "dropped", "added", "rejected"]); assert_eq!(txq.light_status().transaction_count, 2); @@ -704,8 +709,8 @@ mod listener { let mut txq = Pool::new(listener, DummyScoring::default(), Options::default()); // insert - let tx1 = txq.import(b.tx().nonce(1).new()).unwrap(); - let tx2 = txq.import(b.tx().nonce(2).new()).unwrap(); + let tx1 = import(&mut txq, b.tx().nonce(1).new()).unwrap(); + let tx2 = import(&mut txq, b.tx().nonce(2).new()).unwrap(); // then txq.remove(&tx1.hash(), false); @@ -723,8 +728,8 @@ mod listener { let mut txq = Pool::new(listener, DummyScoring::default(), Options::default()); // insert - txq.import(b.tx().nonce(1).new()).unwrap(); - txq.import(b.tx().nonce(2).new()).unwrap(); + import(&mut txq, b.tx().nonce(1).new()).unwrap(); + import(&mut txq, b.tx().nonce(2).new()).unwrap(); // when txq.clear(); @@ -741,8 +746,8 @@ mod listener { let mut txq = Pool::new(listener, DummyScoring::default(), Options::default()); // insert - txq.import(b.tx().nonce(1).new()).unwrap(); - txq.import(b.tx().nonce(2).new()).unwrap(); + import(&mut txq, b.tx().nonce(1).new()).unwrap(); + import(&mut txq, b.tx().nonce(2).new()).unwrap(); // when txq.cull(None, NonceReady::new(3));