[frontport] CLI to specify queue ordering strategy (#2494) (#2623)

* CLI to specify queue ordering strategy (#2494)

* Alter gas priorities to include gas_price also

* CLI options and tests

* Adding ordering by gas

* whitespace

Conflicts:
	ethcore/src/miner/miner.rs
	ethcore/src/miner/mod.rs
	ethcore/src/miner/transaction_queue.rs
	parity/cli/usage.txt
	parity/configuration.rs

* fix build
This commit is contained in:
Tomasz Drwięga 2016-10-15 14:46:33 +02:00 committed by Gav Wood
parent 03c1559ead
commit cceca916a1
11 changed files with 288 additions and 103 deletions

View File

@ -30,7 +30,7 @@ use transaction::{Action, SignedTransaction};
use receipt::{Receipt, RichReceipt}; use receipt::{Receipt, RichReceipt};
use spec::Spec; use spec::Spec;
use engines::Engine; use engines::Engine;
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin}; use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
use miner::work_notify::WorkPoster; use miner::work_notify::WorkPoster;
use client::TransactionImportResult; use client::TransactionImportResult;
use miner::price_info::PriceInfo; use miner::price_info::PriceInfo;
@ -76,6 +76,8 @@ pub struct MinerOptions {
pub tx_gas_limit: U256, pub tx_gas_limit: U256,
/// Maximum size of the transaction queue. /// Maximum size of the transaction queue.
pub tx_queue_size: usize, pub tx_queue_size: usize,
/// Strategy to use for prioritizing transactions in the queue.
pub tx_queue_strategy: PrioritizationStrategy,
/// Whether we should fallback to providing all the queue's transactions or just pending. /// Whether we should fallback to providing all the queue's transactions or just pending.
pub pending_set: PendingSet, pub pending_set: PendingSet,
/// How many historical work packages can we store before running out? /// How many historical work packages can we store before running out?
@ -94,12 +96,13 @@ impl Default for MinerOptions {
reseal_on_external_tx: false, reseal_on_external_tx: false,
reseal_on_own_tx: true, reseal_on_own_tx: true,
tx_gas_limit: !U256::zero(), tx_gas_limit: !U256::zero(),
tx_queue_size: 2048, tx_queue_size: 1024,
tx_queue_gas_limit: GasLimit::Auto,
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
pending_set: PendingSet::AlwaysQueue, pending_set: PendingSet::AlwaysQueue,
reseal_min_period: Duration::from_secs(2), reseal_min_period: Duration::from_secs(2),
work_queue_size: 20, work_queue_size: 20,
enable_resubmission: true, enable_resubmission: true,
tx_queue_gas_limit: GasLimit::Auto,
} }
} }
} }
@ -212,7 +215,9 @@ impl Miner {
GasLimit::Fixed(ref limit) => *limit, GasLimit::Fixed(ref limit) => *limit,
_ => !U256::zero(), _ => !U256::zero(),
}; };
let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(options.tx_queue_size, gas_limit, options.tx_gas_limit))); let txq = Arc::new(Mutex::new(TransactionQueue::with_limits(
options.tx_queue_strategy, options.tx_queue_size, gas_limit, options.tx_gas_limit
)));
Miner { Miner {
transaction_queue: txq, transaction_queue: txq,
next_allowed_reseal: Mutex::new(Instant::now()), next_allowed_reseal: Mutex::new(Instant::now()),
@ -1029,7 +1034,7 @@ impl MinerService for Miner {
mod tests { mod tests {
use std::time::Duration; use std::time::Duration;
use super::super::MinerService; use super::super::{MinerService, PrioritizationStrategy};
use super::*; use super::*;
use util::*; use util::*;
use ethkey::{Generator, Random}; use ethkey::{Generator, Random};
@ -1083,6 +1088,7 @@ mod tests {
tx_gas_limit: !U256::zero(), tx_gas_limit: !U256::zero(),
tx_queue_size: 1024, tx_queue_size: 1024,
tx_queue_gas_limit: GasLimit::None, tx_queue_gas_limit: GasLimit::None,
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
pending_set: PendingSet::AlwaysSealing, pending_set: PendingSet::AlwaysSealing,
work_queue_size: 5, work_queue_size: 5,
enable_resubmission: true, enable_resubmission: true,

View File

@ -47,7 +47,7 @@ mod transaction_queue;
mod work_notify; mod work_notify;
mod price_info; mod price_info;
pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionOrigin}; pub use self::transaction_queue::{TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
pub use self::miner::{Miner, MinerOptions, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit}; pub use self::miner::{Miner, MinerOptions, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
pub use self::external::{ExternalMiner, ExternalMinerService}; pub use self::external::{ExternalMiner, ExternalMinerService};
pub use client::TransactionImportResult; pub use client::TransactionImportResult;

View File

@ -49,7 +49,7 @@
//! balance: U256::from(1_000_000), //! balance: U256::from(1_000_000),
//! }; //! };
//! //!
//! let mut txq = TransactionQueue::new(); //! let mut txq = TransactionQueue::default();
//! txq.add(st2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); //! txq.add(st2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
//! txq.add(st1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); //! txq.add(st1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
//! //!
@ -130,11 +130,20 @@ struct TransactionOrder {
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5) /// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
/// High nonce_height = Low priority (processed later) /// High nonce_height = Low priority (processed later)
nonce_height: U256, nonce_height: U256,
/// Gas specified in the transaction.
gas: U256,
/// Gas Price of the transaction. /// Gas Price of the transaction.
/// Low gas price = Low priority (processed later) /// Low gas price = Low priority (processed later)
gas_price: U256, gas_price: U256,
/// Gas usage priority factor. Usage depends on strategy.
/// Represents the linear increment in required gas price for heavy transactions.
///
/// High gas limit + Low gas price = Low priority
/// High gas limit + High gas price = High priority
gas_factor: U256,
/// Gas (limit) of the transaction. Usage depends on strategy.
/// Low gas limit = High priority (processed earlier)
gas: U256,
/// Transaction ordering strategy
strategy: PrioritizationStrategy,
/// Hash to identify associated transaction /// Hash to identify associated transaction
hash: H256, hash: H256,
/// Origin of the transaction /// Origin of the transaction
@ -145,11 +154,15 @@ struct TransactionOrder {
impl TransactionOrder { impl TransactionOrder {
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256, min_gas_price: U256, strategy: PrioritizationStrategy) -> Self {
let factor = (tx.transaction.gas >> 15) * min_gas_price;
TransactionOrder { TransactionOrder {
nonce_height: tx.nonce() - base_nonce, nonce_height: tx.nonce() - base_nonce,
gas: tx.transaction.gas.clone(),
gas_price: tx.transaction.gas_price, gas_price: tx.transaction.gas_price,
gas: tx.transaction.gas,
gas_factor: factor,
strategy: strategy,
hash: tx.hash(), hash: tx.hash(),
origin: tx.origin, origin: tx.origin,
penalties: 0, penalties: 0,
@ -197,11 +210,28 @@ impl Ord for TransactionOrder {
return self.origin.cmp(&b.origin); return self.origin.cmp(&b.origin);
} }
match self.strategy {
PrioritizationStrategy::GasAndGasPrice => {
if self.gas != b.gas {
return self.gas.cmp(&b.gas);
}
},
PrioritizationStrategy::GasFactorAndGasPrice => {
// avoiding overflows
// (gp1 - g1) > (gp2 - g2) <=>
// (gp1 + g2) > (gp2 + g1)
let f_a = self.gas_price + b.gas_factor;
let f_b = b.gas_price + self.gas_factor;
if f_a != f_b {
return f_b.cmp(&f_a);
}
},
PrioritizationStrategy::GasPriceOnly => {},
}
// Then compare gas_prices // Then compare gas_prices
let a_gas = self.gas_price; if self.gas_price != b.gas_price {
let b_gas = b.gas_price; return b.gas_price.cmp(&self.gas_price);
if a_gas != b_gas {
return b_gas.cmp(&a_gas);
} }
// Compare hashes // Compare hashes
@ -415,8 +445,32 @@ pub struct AccountDetails {
/// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue. /// Transactions with `gas > (gas_limit + gas_limit * Factor(in percents))` are not imported to the queue.
const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) % const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) %
/// Describes the strategy used to prioritize transactions in the queue.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PrioritizationStrategy {
/// Use only gas price. Disregards the actual computation cost of the transaction.
/// i.e. Higher gas price = Higher priority
GasPriceOnly,
/// Use gas limit and then gas price.
/// i.e. Higher gas limit = Lower priority
GasAndGasPrice,
/// Calculate and use priority based on gas and gas price.
/// PRIORITY = GAS_PRICE - GAS/2^15 * MIN_GAS_PRICE
///
/// Rationale:
/// Heavy transactions are paying linear cost (GAS * GAS_PRICE)
/// while the computation might be more expensive.
///
/// i.e.
/// 1M gas tx with `gas_price=30*min` has the same priority
/// as 32k gas tx with `gas_price=min`
GasFactorAndGasPrice,
}
/// `TransactionQueue` implementation /// `TransactionQueue` implementation
pub struct TransactionQueue { pub struct TransactionQueue {
/// Prioritization strategy for this queue
strategy: PrioritizationStrategy,
/// 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)
minimal_gas_price: U256, minimal_gas_price: U256,
/// The maximum amount of gas any individual transaction may use. /// The maximum amount of gas any individual transaction may use.
@ -435,18 +489,18 @@ pub struct TransactionQueue {
impl Default for TransactionQueue { impl Default for TransactionQueue {
fn default() -> Self { fn default() -> Self {
TransactionQueue::new() TransactionQueue::new(PrioritizationStrategy::GasPriceOnly)
} }
} }
impl TransactionQueue { impl TransactionQueue {
/// Creates new instance of this Queue /// Creates new instance of this Queue
pub fn new() -> Self { pub fn new(strategy: PrioritizationStrategy) -> Self {
Self::with_limits(1024, !U256::zero(), !U256::zero()) Self::with_limits(strategy, 1024, !U256::zero(), !U256::zero())
} }
/// Create new instance of this Queue with specified limits /// Create new instance of this Queue with specified limits
pub fn with_limits(limit: usize, gas_limit: U256, tx_gas_limit: U256) -> Self { pub fn with_limits(strategy: PrioritizationStrategy, limit: usize, gas_limit: U256, tx_gas_limit: U256) -> Self {
let current = TransactionSet { let current = TransactionSet {
by_priority: BTreeSet::new(), by_priority: BTreeSet::new(),
by_address: Table::new(), by_address: Table::new(),
@ -464,6 +518,7 @@ impl TransactionQueue {
}; };
TransactionQueue { TransactionQueue {
strategy: strategy,
minimal_gas_price: U256::zero(), minimal_gas_price: U256::zero(),
tx_gas_limit: tx_gas_limit, tx_gas_limit: tx_gas_limit,
gas_limit: !U256::zero(), gas_limit: !U256::zero(),
@ -844,6 +899,7 @@ impl TransactionQueue {
return Err(TransactionError::AlreadyImported); return Err(TransactionError::AlreadyImported);
} }
let min_gas_price = (self.minimal_gas_price, self.strategy);
let address = tx.sender(); let address = tx.sender();
let nonce = tx.nonce(); let nonce = tx.nonce();
let hash = tx.hash(); let hash = tx.hash();
@ -881,7 +937,7 @@ impl TransactionQueue {
if nonce > next_nonce { if nonce > next_nonce {
// We have a gap - put to future. // We have a gap - put to future.
// Insert transaction (or replace old one with lower gas price) // Insert transaction (or replace old one with lower gas price)
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.future, &mut self.by_hash))); try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.future, &mut self.by_hash)));
// Enforce limit in Future // Enforce limit in Future
let removed = self.future.enforce_limit(&mut self.by_hash); let removed = self.future.enforce_limit(&mut self.by_hash);
// Return an error if this transaction was not imported because of limit. // Return an error if this transaction was not imported because of limit.
@ -897,7 +953,7 @@ impl TransactionQueue {
self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce); self.move_matching_future_to_current(address, nonce + U256::one(), state_nonce);
// Replace transaction if any // Replace transaction if any
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash))); try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, min_gas_price, &mut self.current, &mut self.by_hash)));
// Keep track of highest nonce stored in current // Keep track of highest nonce stored in current
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n)); let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
self.last_nonces.insert(address, new_max); self.last_nonces.insert(address, new_max);
@ -934,8 +990,8 @@ impl TransactionQueue {
/// ///
/// Returns `true` if transaction actually got to the queue (`false` if there was already a transaction with higher /// Returns `true` if transaction actually got to the queue (`false` if there was already a transaction with higher
/// gas_price) /// gas_price)
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool { fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, min_gas_price: (U256, PrioritizationStrategy), set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
let order = TransactionOrder::for_transaction(&tx, base_nonce); let order = TransactionOrder::for_transaction(&tx, base_nonce, min_gas_price.0, min_gas_price.1);
let hash = tx.hash(); let hash = tx.hash();
let address = tx.sender(); let address = tx.sender();
let nonce = tx.nonce(); let nonce = tx.nonce();
@ -1015,12 +1071,12 @@ mod test {
fn default_gas_val() -> U256 { 100_000.into() } fn default_gas_val() -> U256 { 100_000.into() }
fn default_gas_price() -> U256 { 1.into() } fn default_gas_price() -> U256 { 1.into() }
fn new_unsigned_tx(nonce: U256, gas_price: U256) -> Transaction { fn new_unsigned_tx(nonce: U256, gas: U256, gas_price: U256) -> Transaction {
Transaction { Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(100), value: U256::from(100),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: default_gas_val(), gas: gas,
gas_price: gas_price, gas_price: gas_price,
nonce: nonce nonce: nonce
} }
@ -1028,7 +1084,12 @@ mod test {
fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction { fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
new_unsigned_tx(nonce, gas_price).sign(keypair.secret()) new_unsigned_tx(nonce, default_gas_val(), gas_price).sign(keypair.secret())
}
fn new_tx_with_gas(gas: U256, gas_price: U256) -> SignedTransaction {
let keypair = Random.generate().unwrap();
new_unsigned_tx(default_nonce(), gas, gas_price).sign(keypair.secret())
} }
fn new_tx_default() -> SignedTransaction { fn new_tx_default() -> SignedTransaction {
@ -1043,8 +1104,8 @@ mod test {
} }
fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
let tx1 = new_unsigned_tx(nonce, gas_price); let tx1 = new_unsigned_tx(nonce, default_gas_val(), gas_price);
let tx2 = new_unsigned_tx(nonce + nonce_increment, gas_price + gas_price_increment); let tx2 = new_unsigned_tx(nonce + nonce_increment, default_gas_val(), gas_price + gas_price_increment);
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let secret = &keypair.secret(); let secret = &keypair.secret();
@ -1054,8 +1115,8 @@ mod test {
/// Returns two consecutive transactions, both with increased gas price /// Returns two consecutive transactions, both with increased gas price
fn new_tx_pair_with_gas_price_increment(gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { fn new_tx_pair_with_gas_price_increment(gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
let gas = default_gas_price() + gas_price_increment; let gas = default_gas_price() + gas_price_increment;
let tx1 = new_unsigned_tx(default_nonce(), gas); let tx1 = new_unsigned_tx(default_nonce(), default_gas_val(), gas);
let tx2 = new_unsigned_tx(default_nonce() + 1.into(), gas); let tx2 = new_unsigned_tx(default_nonce() + 1.into(), default_gas_val(), gas);
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let secret = &keypair.secret(); let secret = &keypair.secret();
@ -1082,17 +1143,21 @@ mod test {
assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater); assert_eq!(TransactionOrigin::External.cmp(&TransactionOrigin::RetractedBlock), Ordering::Greater);
} }
fn transaction_order(tx: &VerifiedTransaction, nonce: U256) -> TransactionOrder {
TransactionOrder::for_transaction(tx, nonce, 0.into(), PrioritizationStrategy::GasPriceOnly)
}
#[test] #[test]
fn should_return_correct_nonces_when_dropped_because_of_limit() { fn should_return_correct_nonces_when_dropped_because_of_limit() {
// given // given
let mut txq = TransactionQueue::with_limits(2, !U256::zero(), !U256::zero()); let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 2, !U256::zero(), !U256::zero());
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into()); let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
let sender = tx1.sender().unwrap(); let sender = tx1.sender().unwrap();
let nonce = tx1.nonce; let nonce = tx1.nonce;
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
assert_eq!(txq.status().pending, 2); assert_eq!(txq.status().pending, 2);
assert_eq!(txq.last_nonce(&sender), Some(nonce + U256::one())); assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into()));
// when // when
let tx = new_tx(123.into(), 1.into()); let tx = new_tx(123.into(), 1.into());
@ -1138,9 +1203,9 @@ mod test {
x x
}; };
// Insert both transactions // Insert both transactions
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero()); let order1 = transaction_order(&tx1, U256::zero());
set.insert(tx1.sender(), tx1.nonce(), order1.clone()); set.insert(tx1.sender(), tx1.nonce(), order1.clone());
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero()); let order2 = transaction_order(&tx2, U256::zero());
set.insert(tx2.sender(), tx2.nonce(), order2.clone()); set.insert(tx2.sender(), tx2.nonce(), order2.clone());
assert_eq!(set.by_priority.len(), 2); assert_eq!(set.by_priority.len(), 2);
assert_eq!(set.by_address.len(), 2); assert_eq!(set.by_address.len(), 2);
@ -1181,7 +1246,7 @@ mod test {
x x
}; };
// Insert both transactions // Insert both transactions
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero()); let order1 = transaction_order(&tx1, U256::zero());
set.insert(tx1.sender(), tx1.nonce(), order1.clone()); set.insert(tx1.sender(), tx1.nonce(), order1.clone());
assert_eq!(set.by_priority.len(), 1); assert_eq!(set.by_priority.len(), 1);
assert_eq!(set.by_address.len(), 1); assert_eq!(set.by_address.len(), 1);
@ -1189,7 +1254,7 @@ mod test {
assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into()); assert_eq!(*set.by_gas_price.iter().next().unwrap().0, 1.into());
assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1); assert_eq!(set.by_gas_price.iter().next().unwrap().1.len(), 1);
// Two different orders (imagine nonce changed in the meantime) // Two different orders (imagine nonce changed in the meantime)
let order2 = TransactionOrder::for_transaction(&tx2, U256::one()); let order2 = transaction_order(&tx2, U256::one());
set.insert(tx2.sender(), tx2.nonce(), order2.clone()); set.insert(tx2.sender(), tx2.nonce(), order2.clone());
assert_eq!(set.by_priority.len(), 1); assert_eq!(set.by_priority.len(), 1);
assert_eq!(set.by_address.len(), 1); assert_eq!(set.by_address.len(), 1);
@ -1218,10 +1283,10 @@ mod test {
}; };
let tx = new_tx_default(); let tx = new_tx_default();
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap(); let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero()); let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none()); assert!(set.insert(tx1.sender(), tx1.nonce(), order1).is_none());
let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External).unwrap(); let tx2 = VerifiedTransaction::new(tx, TransactionOrigin::External).unwrap();
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero()); let order2 = TransactionOrder::for_transaction(&tx2, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some()); assert!(set.insert(tx2.sender(), tx2.nonce(), order2).is_some());
} }
@ -1238,7 +1303,7 @@ mod test {
assert_eq!(set.gas_price_entry_limit(), 0.into()); assert_eq!(set.gas_price_entry_limit(), 0.into());
let tx = new_tx_default(); let tx = new_tx_default();
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap(); let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External).unwrap();
let order1 = TransactionOrder::for_transaction(&tx1, U256::zero()); let order1 = TransactionOrder::for_transaction(&tx1, 0.into(), 1.into(), PrioritizationStrategy::GasPriceOnly);
assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none()); assert!(set.insert(tx1.sender(), tx1.nonce(), order1.clone()).is_none());
assert_eq!(set.gas_price_entry_limit(), 2.into()); assert_eq!(set.gas_price_entry_limit(), 2.into());
} }
@ -1246,7 +1311,7 @@ mod test {
#[test] #[test]
fn should_handle_same_transaction_imported_twice_with_different_state_nonces() { fn should_handle_same_transaction_imported_twice_with_different_state_nonces() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_similar_tx_pair(); let (tx, tx2) = new_similar_tx_pair();
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
!U256::zero() }; !U256::zero() };
@ -1271,7 +1336,7 @@ mod test {
#[test] #[test]
fn should_move_all_transactions_from_future() { fn should_move_all_transactions_from_future() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 1.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 1.into());
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
!U256::zero() }; !U256::zero() };
@ -1297,7 +1362,7 @@ mod test {
#[test] #[test]
fn should_import_tx() { fn should_import_tx() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
// when // when
@ -1309,10 +1374,77 @@ mod test {
assert_eq!(stats.pending, 1); assert_eq!(stats.pending, 1);
} }
#[test]
fn should_order_by_gas() {
// given
let mut txq = TransactionQueue::new(PrioritizationStrategy::GasAndGasPrice);
let tx1 = new_tx_with_gas(50000.into(), 40.into());
let tx2 = new_tx_with_gas(40000.into(), 30.into());
let tx3 = new_tx_with_gas(30000.into(), 10.into());
let tx4 = new_tx_with_gas(50000.into(), 20.into());
txq.set_minimal_gas_price(15.into());
// when
let res1 = txq.add(tx1, &default_account_details, TransactionOrigin::External);
let res2 = txq.add(tx2, &default_account_details, TransactionOrigin::External);
let res3 = txq.add(tx3, &default_account_details, TransactionOrigin::External);
let res4 = txq.add(tx4, &default_account_details, TransactionOrigin::External);
// then
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
assert_eq!(res2.unwrap(), TransactionImportResult::Current);
assert_eq!(unwrap_tx_err(res3), TransactionError::InsufficientGasPrice {
minimal: U256::from(15),
got: U256::from(10),
});
assert_eq!(res4.unwrap(), TransactionImportResult::Current);
let stats = txq.status();
assert_eq!(stats.pending, 3);
assert_eq!(txq.top_transactions()[0].gas, 40000.into());
assert_eq!(txq.top_transactions()[1].gas, 50000.into());
assert_eq!(txq.top_transactions()[2].gas, 50000.into());
assert_eq!(txq.top_transactions()[1].gas_price, 40.into());
assert_eq!(txq.top_transactions()[2].gas_price, 20.into());
}
#[test]
fn should_order_by_gas_factor() {
// given
let mut txq = TransactionQueue::new(PrioritizationStrategy::GasFactorAndGasPrice);
let tx1 = new_tx_with_gas(150_000.into(), 40.into());
let tx2 = new_tx_with_gas(40_000.into(), 16.into());
let tx3 = new_tx_with_gas(30_000.into(), 15.into());
let tx4 = new_tx_with_gas(150_000.into(), 62.into());
txq.set_minimal_gas_price(15.into());
// when
let res1 = txq.add(tx1, &default_account_details, TransactionOrigin::External);
let res2 = txq.add(tx2, &default_account_details, TransactionOrigin::External);
let res3 = txq.add(tx3, &default_account_details, TransactionOrigin::External);
let res4 = txq.add(tx4, &default_account_details, TransactionOrigin::External);
// then
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
assert_eq!(res2.unwrap(), TransactionImportResult::Current);
assert_eq!(res3.unwrap(), TransactionImportResult::Current);
assert_eq!(res4.unwrap(), TransactionImportResult::Current);
let stats = txq.status();
assert_eq!(stats.pending, 4);
assert_eq!(txq.top_transactions()[0].gas, 30_000.into());
assert_eq!(txq.top_transactions()[1].gas, 150_000.into());
assert_eq!(txq.top_transactions()[2].gas, 40_000.into());
assert_eq!(txq.top_transactions()[3].gas, 150_000.into());
assert_eq!(txq.top_transactions()[0].gas_price, 15.into());
assert_eq!(txq.top_transactions()[1].gas_price, 62.into());
assert_eq!(txq.top_transactions()[2].gas_price, 16.into());
assert_eq!(txq.top_transactions()[3].gas_price, 40.into());
}
#[test] #[test]
fn gas_limit_should_never_overflow() { fn gas_limit_should_never_overflow() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
txq.set_gas_limit(U256::zero()); txq.set_gas_limit(U256::zero());
assert_eq!(txq.gas_limit, U256::zero()); assert_eq!(txq.gas_limit, U256::zero());
@ -1326,7 +1458,7 @@ mod test {
#[test] #[test]
fn should_not_import_transaction_above_gas_limit() { fn should_not_import_transaction_above_gas_limit() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
let gas = tx.gas; let gas = tx.gas;
let limit = gas / U256::from(2); let limit = gas / U256::from(2);
@ -1349,7 +1481,7 @@ mod test {
#[test] #[test]
fn should_drop_transactions_from_senders_without_balance() { fn should_drop_transactions_from_senders_without_balance() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
let account = |a: &Address| AccountDetails { let account = |a: &Address| AccountDetails {
nonce: default_account_details(a).nonce, nonce: default_account_details(a).nonce,
@ -1372,7 +1504,7 @@ mod test {
#[test] #[test]
fn should_not_import_transaction_below_min_gas_price_threshold_if_external() { fn should_not_import_transaction_below_min_gas_price_threshold_if_external() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
txq.set_minimal_gas_price(tx.gas_price + U256::one()); txq.set_minimal_gas_price(tx.gas_price + U256::one());
@ -1392,7 +1524,7 @@ mod test {
#[test] #[test]
fn should_import_transaction_below_min_gas_price_threshold_if_local() { fn should_import_transaction_below_min_gas_price_threshold_if_local() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
txq.set_minimal_gas_price(tx.gas_price + U256::one()); txq.set_minimal_gas_price(tx.gas_price + U256::one());
@ -1411,8 +1543,8 @@ mod test {
use rlp::{self, RlpStream, Stream}; use rlp::{self, RlpStream, Stream};
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_unsigned_tx(123.into(), 1.into()); let tx = new_unsigned_tx(123.into(), 100.into(), 1.into());
let stx = { let stx = {
let mut s = RlpStream::new_list(9); let mut s = RlpStream::new_list(9);
s.append(&tx.nonce); s.append(&tx.nonce);
@ -1436,7 +1568,7 @@ mod test {
#[test] #[test]
fn should_import_txs_from_same_sender() { fn should_import_txs_from_same_sender() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
@ -1454,7 +1586,7 @@ mod test {
#[test] #[test]
fn should_prioritize_local_transactions_within_same_nonce_height() { fn should_prioritize_local_transactions_within_same_nonce_height() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
// the second one has same nonce but higher `gas_price` // the second one has same nonce but higher `gas_price`
let (_, tx2) = new_similar_tx_pair(); let (_, tx2) = new_similar_tx_pair();
@ -1475,7 +1607,7 @@ mod test {
#[test] #[test]
fn should_prioritize_reimported_transactions_within_same_nonce_height() { fn should_prioritize_reimported_transactions_within_same_nonce_height() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
// the second one has same nonce but higher `gas_price` // the second one has same nonce but higher `gas_price`
let (_, tx2) = new_similar_tx_pair(); let (_, tx2) = new_similar_tx_pair();
@ -1496,7 +1628,7 @@ mod test {
#[test] #[test]
fn should_not_prioritize_local_transactions_with_different_nonce_height() { fn should_not_prioritize_local_transactions_with_different_nonce_height() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
// when // when
@ -1514,7 +1646,7 @@ mod test {
fn should_penalize_transactions_from_sender_in_future() { fn should_penalize_transactions_from_sender_in_future() {
// given // given
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() }; let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() };
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
// txa, txb - slightly bigger gas price to have consistent ordering // txa, txb - slightly bigger gas price to have consistent ordering
let (txa, txb) = new_tx_pair_default(1.into(), 0.into()); let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
@ -1543,7 +1675,7 @@ mod test {
#[test] #[test]
fn should_penalize_transactions_from_sender() { fn should_penalize_transactions_from_sender() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
// txa, txb - slightly bigger gas price to have consistent ordering // txa, txb - slightly bigger gas price to have consistent ordering
let (txa, txb) = new_tx_pair_default(1.into(), 0.into()); let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
@ -1576,7 +1708,7 @@ mod test {
#[test] #[test]
fn should_return_pending_hashes() { fn should_return_pending_hashes() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
@ -1594,7 +1726,7 @@ mod test {
#[test] #[test]
fn should_put_transaction_to_futures_if_gap_detected() { fn should_put_transaction_to_futures_if_gap_detected() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(2.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(2.into(), 0.into());
@ -1620,7 +1752,7 @@ mod test {
!U256::zero() }; !U256::zero() };
let next2_nonce = default_nonce() + U256::from(3); let next2_nonce = default_nonce() + U256::from(3);
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap(); txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
@ -1639,12 +1771,12 @@ mod test {
#[test] #[test]
fn should_move_transactions_if_gap_filled() { fn should_move_transactions_if_gap_filled() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let secret = kp.secret(); let secret = kp.secret();
let tx = new_unsigned_tx(123.into(), 1.into()).sign(secret); let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(secret);
let tx1 = new_unsigned_tx(124.into(), 1.into()).sign(secret); let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret);
let tx2 = new_unsigned_tx(125.into(), 1.into()).sign(secret); let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret);
txq.add(tx, &default_account_details, TransactionOrigin::External).unwrap(); txq.add(tx, &default_account_details, TransactionOrigin::External).unwrap();
assert_eq!(txq.status().pending, 1); assert_eq!(txq.status().pending, 1);
@ -1666,7 +1798,7 @@ mod test {
#[test] #[test]
fn should_remove_transaction() { fn should_remove_transaction() {
// given // given
let mut txq2 = TransactionQueue::new(); let mut txq2 = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(3.into(), 0.into());
txq2.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq2.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq2.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq2.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
@ -1687,7 +1819,7 @@ mod test {
#[test] #[test]
fn should_move_transactions_to_future_if_gap_introduced() { fn should_move_transactions_to_future_if_gap_introduced() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
let tx3 = new_tx_default(); let tx3 = new_tx_default();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
@ -1708,7 +1840,7 @@ mod test {
#[test] #[test]
fn should_clear_queue() { fn should_clear_queue() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
// add // add
@ -1728,7 +1860,7 @@ mod test {
#[test] #[test]
fn should_drop_old_transactions_when_hitting_the_limit() { fn should_drop_old_transactions_when_hitting_the_limit() {
// given // given
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero()); let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
let sender = tx.sender().unwrap(); let sender = tx.sender().unwrap();
let nonce = tx.nonce; let nonce = tx.nonce;
@ -1749,7 +1881,7 @@ mod test {
#[test] #[test]
fn should_limit_future_transactions() { fn should_limit_future_transactions() {
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero()); let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
txq.current.set_limit(10); txq.current.set_limit(10);
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into()); let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into()); let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
@ -1768,19 +1900,20 @@ mod test {
#[test] #[test]
fn should_limit_by_gas() { fn should_limit_by_gas() {
let mut txq = TransactionQueue::with_limits(100, default_gas_val() * U256::from(2), !U256::zero()); let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).ok(); txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).ok(); txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).ok(); txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(tx4.clone(), &default_account_details, TransactionOrigin::External).ok(); // limited by gas
txq.add(tx4.clone(), &default_account_details, TransactionOrigin::External).unwrap_err();
assert_eq!(txq.status().pending, 2); assert_eq!(txq.status().pending, 2);
} }
#[test] #[test]
fn should_keep_own_transactions_above_gas_limit() { fn should_keep_own_transactions_above_gas_limit() {
let mut txq = TransactionQueue::with_limits(100, default_gas_val() * U256::from(2), !U256::zero()); let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
let (tx5, tx6) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx5, tx6) = new_tx_pair_default(U256::from(1), U256::from(2));
@ -1796,10 +1929,10 @@ mod test {
#[test] #[test]
fn should_drop_transactions_with_old_nonces() { fn should_drop_transactions_with_old_nonces() {
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
let last_nonce = tx.nonce + U256::one(); let last_nonce = tx.nonce + U256::one();
let fetch_last_nonce = |_a: &Address| AccountDetails{ nonce: last_nonce, balance: !U256::zero() }; let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() };
// when // when
let res = txq.add(tx, &fetch_last_nonce, TransactionOrigin::External); let res = txq.add(tx, &fetch_last_nonce, TransactionOrigin::External);
@ -1816,7 +1949,7 @@ mod test {
// given // given
let nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce + U256::one(), let nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce + U256::one(),
balance: !U256::zero() }; balance: !U256::zero() };
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
assert_eq!(txq.status().future, 1); assert_eq!(txq.status().future, 1);
@ -1835,7 +1968,7 @@ mod test {
#[test] #[test]
fn should_accept_same_transaction_twice_if_removed() { fn should_accept_same_transaction_twice_if_removed() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
@ -1856,7 +1989,7 @@ 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 mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
let tx3 = new_tx_default(); let tx3 = new_tx_default();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
@ -1879,9 +2012,9 @@ mod test {
fn should_replace_same_transaction_when_has_higher_fee() { fn should_replace_same_transaction_when_has_higher_fee() {
init_log(); init_log();
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let tx = new_unsigned_tx(123.into(), 1.into()).sign(keypair.secret()); let tx = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret());
let tx2 = { let tx2 = {
let mut tx2 = (*tx).clone(); let mut tx2 = (*tx).clone();
tx2.gas_price = U256::from(200); tx2.gas_price = U256::from(200);
@ -1902,9 +2035,9 @@ mod test {
#[test] #[test]
fn should_replace_same_transaction_when_importing_to_futures() { fn should_replace_same_transaction_when_importing_to_futures() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let tx0 = new_unsigned_tx(123.into(), 1.into()).sign(keypair.secret()); let tx0 = new_unsigned_tx(123.into(), default_gas_val(), 1.into()).sign(keypair.secret());
let tx1 = { let tx1 = {
let mut tx1 = (*tx0).clone(); let mut tx1 = (*tx0).clone();
tx1.nonce = U256::from(124); tx1.nonce = U256::from(124);
@ -1936,7 +2069,7 @@ mod test {
!U256::zero() }; !U256::zero() };
let next_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce + U256::one(), balance: let next_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce + U256::one(), balance:
!U256::zero() }; !U256::zero() };
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap(); txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap();
txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap(); txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap();
@ -1954,7 +2087,7 @@ mod test {
#[test] #[test]
fn should_return_none_when_transaction_from_given_address_does_not_exist() { fn should_return_none_when_transaction_from_given_address_does_not_exist() {
// given // given
let txq = TransactionQueue::new(); let txq = TransactionQueue::default();
// then // then
assert_eq!(txq.last_nonce(&Address::default()), None); assert_eq!(txq.last_nonce(&Address::default()), None);
@ -1963,7 +2096,7 @@ mod test {
#[test] #[test]
fn should_return_correct_nonce_when_transactions_from_given_address_exist() { fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let tx = new_tx_default(); let tx = new_tx_default();
let from = tx.sender().unwrap(); let from = tx.sender().unwrap();
let nonce = tx.nonce; let nonce = tx.nonce;
@ -1979,7 +2112,7 @@ mod test {
#[test] #[test]
fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() { fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce); let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() }; let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
@ -1997,7 +2130,7 @@ mod test {
#[test] #[test]
fn should_return_valid_last_nonce_after_remove_all() { fn should_return_valid_last_nonce_after_remove_all() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into()); let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into());
let sender = tx1.sender().unwrap(); let sender = tx1.sender().unwrap();
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce); let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
@ -2021,7 +2154,7 @@ mod test {
#[test] #[test]
fn should_return_true_if_there_is_local_transaction_pending() { fn should_return_true_if_there_is_local_transaction_pending() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
assert_eq!(txq.has_local_pending_transactions(), false); assert_eq!(txq.has_local_pending_transactions(), false);
@ -2037,7 +2170,7 @@ mod test {
#[test] #[test]
fn should_keep_right_order_in_future() { fn should_keep_right_order_in_future() {
// given // given
let mut txq = TransactionQueue::with_limits(1, !U256::zero(), !U256::zero()); let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance: let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
default_account_details(a).balance }; default_account_details(a).balance };
@ -2054,15 +2187,16 @@ mod test {
#[test] #[test]
fn should_return_correct_last_nonce() { fn should_return_correct_last_nonce() {
// given // given
let mut txq = TransactionQueue::new(); let mut txq = TransactionQueue::default();
let (tx1, tx2, tx2_2, tx3) = { let (tx1, tx2, tx2_2, tx3) = {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let secret = &keypair.secret(); let secret = &keypair.secret();
let nonce = 123.into(); let nonce = 123.into();
let tx = new_unsigned_tx(nonce, 1.into()); let gas = default_gas_val();
let tx2 = new_unsigned_tx(nonce + 1.into(), 1.into()); let tx = new_unsigned_tx(nonce, gas, 1.into());
let tx2_2 = new_unsigned_tx(nonce + 1.into(), 5.into()); let tx2 = new_unsigned_tx(nonce + 1.into(), gas, 1.into());
let tx3 = new_unsigned_tx(nonce + 2.into(), 1.into()); let tx2_2 = new_unsigned_tx(nonce + 1.into(), gas, 5.into());
let tx3 = new_unsigned_tx(nonce + 2.into(), gas, 1.into());
(tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret)) (tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret))

View File

@ -67,8 +67,9 @@ usd_per_eth = "auto"
price_update_period = "hourly" price_update_period = "hourly"
gas_floor_target = "4700000" gas_floor_target = "4700000"
gas_cap = "6283184" gas_cap = "6283184"
tx_queue_size = 2048 tx_queue_size = 1024
tx_queue_gas = "auto" tx_queue_gas = "auto"
tx_queue_strategy = "gas_factor"
tx_gas_limit = "6283184" tx_gas_limit = "6283184"
extra_data = "Parity" extra_data = "Parity"
remove_solved = false remove_solved = false

View File

@ -40,7 +40,7 @@ force_sealing = true
reseal_on_txs = "all" reseal_on_txs = "all"
reseal_min_period = 4000 reseal_min_period = 4000
price_update_period = "hourly" price_update_period = "hourly"
tx_queue_size = 2048 tx_queue_size = 1024
tx_queue_gas = "auto" tx_queue_gas = "auto"
[footprint] [footprint]

View File

@ -193,10 +193,12 @@ usage! {
or |c: &Config| otry!(c.mining).gas_cap.clone(), or |c: &Config| otry!(c.mining).gas_cap.clone(),
flag_extra_data: Option<String> = None, flag_extra_data: Option<String> = None,
or |c: &Config| otry!(c.mining).extra_data.clone().map(Some), or |c: &Config| otry!(c.mining).extra_data.clone().map(Some),
flag_tx_queue_size: usize = 2048usize, flag_tx_queue_size: usize = 1024usize,
or |c: &Config| otry!(c.mining).tx_queue_size.clone(), or |c: &Config| otry!(c.mining).tx_queue_size.clone(),
flag_tx_queue_gas: String = "auto", flag_tx_queue_gas: String = "auto",
or |c: &Config| otry!(c.mining).tx_queue_gas.clone(), or |c: &Config| otry!(c.mining).tx_queue_gas.clone(),
flag_tx_queue_strategy: String = "gas_factor",
or |c: &Config| otry!(c.mining).tx_queue_strategy.clone(),
flag_remove_solved: bool = false, flag_remove_solved: bool = false,
or |c: &Config| otry!(c.mining).remove_solved.clone(), or |c: &Config| otry!(c.mining).remove_solved.clone(),
flag_notify_work: Option<String> = None, flag_notify_work: Option<String> = None,
@ -355,6 +357,7 @@ struct Mining {
extra_data: Option<String>, extra_data: Option<String>,
tx_queue_size: Option<usize>, tx_queue_size: Option<usize>,
tx_queue_gas: Option<String>, tx_queue_gas: Option<String>,
tx_queue_strategy: Option<String>,
remove_solved: Option<bool>, remove_solved: Option<bool>,
notify_work: Option<Vec<String>>, notify_work: Option<Vec<String>>,
} }
@ -531,8 +534,9 @@ mod tests {
flag_gas_floor_target: "4700000".into(), flag_gas_floor_target: "4700000".into(),
flag_gas_cap: "6283184".into(), flag_gas_cap: "6283184".into(),
flag_extra_data: Some("Parity".into()), flag_extra_data: Some("Parity".into()),
flag_tx_queue_size: 2048usize, flag_tx_queue_size: 1024usize,
flag_tx_queue_gas: "auto".into(), flag_tx_queue_gas: "auto".into(),
flag_tx_queue_strategy: "gas_factor".into(),
flag_remove_solved: false, flag_remove_solved: false,
flag_notify_work: Some("http://localhost:3001".into()), flag_notify_work: Some("http://localhost:3001".into()),
@ -684,8 +688,9 @@ mod tests {
price_update_period: Some("hourly".into()), price_update_period: Some("hourly".into()),
gas_floor_target: None, gas_floor_target: None,
gas_cap: None, gas_cap: None,
tx_queue_size: Some(2048), tx_queue_size: Some(1024),
tx_queue_gas: Some("auto".into()), tx_queue_gas: Some("auto".into()),
tx_queue_strategy: None,
tx_gas_limit: None, tx_gas_limit: None,
extra_data: None, extra_data: None,
remove_solved: None, remove_solved: None,

View File

@ -188,6 +188,12 @@ Sealing/Mining Options:
the queue. LIMIT can be either an amount of gas or the queue. LIMIT can be either an amount of gas or
'auto' or 'off'. 'auto' sets the limit to be 20x 'auto' or 'off'. 'auto' sets the limit to be 20x
the current block gas limit. (default: {flag_tx_queue_gas}). the current block gas limit. (default: {flag_tx_queue_gas}).
--tx-queue-strategy S Prioritization strategy used to order transactions
in the queue. S may be:
gas - Prioritize txs with low gas limit;
gas_price - Prioritize txs with high gas price;
gas_factor - Prioritize txs using gas price
and gas limit ratio (default: {flag_tx_queue_strategy}).
--remove-solved Move solved blocks from the work package queue --remove-solved Move solved blocks from the work package queue
instead of cloning them. This gives a slightly instead of cloning them. This gives a slightly
faster import speed, but means that extra solutions faster import speed, but means that extra solutions

View File

@ -30,7 +30,7 @@ use rpc::{IpcConfiguration, HttpConfiguration};
use ethcore_rpc::NetworkSettings; use ethcore_rpc::NetworkSettings;
use cache::CacheConfig; use cache::CacheConfig;
use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home, use helpers::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_price, replace_home,
geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_gas_limit}; geth_ipc_path, parity_ipc_path, to_bootnodes, to_addresses, to_address, to_gas_limit, to_queue_strategy};
use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras}; use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras};
use ethcore_logger::Config as LogConfig; use ethcore_logger::Config as LogConfig;
use dir::Directories; use dir::Directories;
@ -360,6 +360,7 @@ impl Configuration {
}, },
tx_queue_size: self.args.flag_tx_queue_size, tx_queue_size: self.args.flag_tx_queue_size,
tx_queue_gas_limit: try!(to_gas_limit(&self.args.flag_tx_queue_gas)), tx_queue_gas_limit: try!(to_gas_limit(&self.args.flag_tx_queue_gas)),
tx_queue_strategy: try!(to_queue_strategy(&self.args.flag_tx_queue_strategy)),
pending_set: try!(to_pending_set(&self.args.flag_relay_set)), pending_set: try!(to_pending_set(&self.args.flag_relay_set)),
reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period), reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period),
work_queue_size: self.args.flag_work_queue_size, work_queue_size: self.args.flag_work_queue_size,
@ -647,6 +648,7 @@ mod tests {
use cli::Args; use cli::Args;
use ethcore_rpc::NetworkSettings; use ethcore_rpc::NetworkSettings;
use ethcore::client::{VMType, BlockID}; use ethcore::client::{VMType, BlockID};
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
use helpers::{replace_home, default_network_config}; use helpers::{replace_home, default_network_config};
use run::RunCmd; use run::RunCmd;
use signer::Configuration as SignerConfiguration; use signer::Configuration as SignerConfiguration;
@ -830,6 +832,27 @@ mod tests {
})); }));
} }
#[test]
fn should_parse_mining_options() {
// given
let mut mining_options = MinerOptions::default();
// when
let conf0 = parse(&["parity"]);
let conf1 = parse(&["parity", "--tx-queue-strategy", "gas_factor"]);
let conf2 = parse(&["parity", "--tx-queue-strategy", "gas_price"]);
let conf3 = parse(&["parity", "--tx-queue-strategy", "gas"]);
// then
assert_eq!(conf0.miner_options().unwrap(), mining_options);
mining_options.tx_queue_strategy = PrioritizationStrategy::GasFactorAndGasPrice;
assert_eq!(conf1.miner_options().unwrap(), mining_options);
mining_options.tx_queue_strategy = PrioritizationStrategy::GasPriceOnly;
assert_eq!(conf2.miner_options().unwrap(), mining_options);
mining_options.tx_queue_strategy = PrioritizationStrategy::GasAndGasPrice;
assert_eq!(conf3.miner_options().unwrap(), mining_options);
}
#[test] #[test]
fn should_parse_network_settings() { fn should_parse_network_settings() {
// given // given

View File

@ -22,7 +22,7 @@ use std::fs::File;
use util::{clean_0x, U256, Uint, Address, path, CompactionProfile}; use util::{clean_0x, U256, Uint, Address, path, CompactionProfile};
use util::journaldb::Algorithm; use util::journaldb::Algorithm;
use ethcore::client::{Mode, BlockID, VMType, DatabaseCompactionProfile, ClientConfig}; use ethcore::client::{Mode, BlockID, VMType, DatabaseCompactionProfile, ClientConfig};
use ethcore::miner::{PendingSet, GasLimit}; use ethcore::miner::{PendingSet, GasLimit, PrioritizationStrategy};
use cache::CacheConfig; use cache::CacheConfig;
use dir::DatabaseDirectories; use dir::DatabaseDirectories;
use upgrade::upgrade; use upgrade::upgrade;
@ -101,6 +101,15 @@ pub fn to_gas_limit(s: &str) -> Result<GasLimit, String> {
} }
} }
pub fn to_queue_strategy(s: &str) -> Result<PrioritizationStrategy, String> {
match s {
"gas" => Ok(PrioritizationStrategy::GasAndGasPrice),
"gas_price" => Ok(PrioritizationStrategy::GasPriceOnly),
"gas_factor" => Ok(PrioritizationStrategy::GasFactorAndGasPrice),
other => Err(format!("Invalid queue strategy: {}", other)),
}
}
pub fn to_address(s: Option<String>) -> Result<Address, String> { pub fn to_address(s: Option<String>) -> Result<Address, String> {
match s { match s {
Some(ref a) => clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)), Some(ref a) => clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)),

View File

@ -206,7 +206,7 @@ impl Default for MinerExtras {
extra_data: version_data(), extra_data: version_data(),
gas_floor_target: U256::from(4_700_000), gas_floor_target: U256::from(4_700_000),
gas_ceil_target: U256::from(6_283_184), gas_ceil_target: U256::from(6_283_184),
transactions_limit: 2048, transactions_limit: 1024,
} }
} }
} }

View File

@ -24,7 +24,7 @@ use ethcore::spec::{Genesis, Spec};
use ethcore::block::Block; use ethcore::block::Block;
use ethcore::views::BlockView; use ethcore::views::BlockView;
use ethcore::ethereum; use ethcore::ethereum;
use ethcore::miner::{MinerOptions, GasPricer, MinerService, ExternalMiner, Miner, PendingSet, GasLimit}; use ethcore::miner::{MinerOptions, GasPricer, MinerService, ExternalMiner, Miner, PendingSet, PrioritizationStrategy, GasLimit};
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use devtools::RandomTempPath; use devtools::RandomTempPath;
use util::Hashable; use util::Hashable;
@ -59,6 +59,7 @@ fn miner_service(spec: &Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
reseal_on_own_tx: true, reseal_on_own_tx: true,
tx_queue_size: 1024, tx_queue_size: 1024,
tx_gas_limit: !U256::zero(), tx_gas_limit: !U256::zero(),
tx_queue_strategy: PrioritizationStrategy::GasPriceOnly,
tx_queue_gas_limit: GasLimit::None, tx_queue_gas_limit: GasLimit::None,
pending_set: PendingSet::SealingOrElseQueue, pending_set: PendingSet::SealingOrElseQueue,
reseal_min_period: Duration::from_secs(0), reseal_min_period: Duration::from_secs(0),