[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 spec::Spec;
use engines::Engine;
use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin};
use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
use miner::work_notify::WorkPoster;
use client::TransactionImportResult;
use miner::price_info::PriceInfo;
@ -76,6 +76,8 @@ pub struct MinerOptions {
pub tx_gas_limit: U256,
/// Maximum size of the transaction queue.
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.
pub pending_set: PendingSet,
/// 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_own_tx: true,
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,
reseal_min_period: Duration::from_secs(2),
work_queue_size: 20,
enable_resubmission: true,
tx_queue_gas_limit: GasLimit::Auto,
}
}
}
@ -212,7 +215,9 @@ impl Miner {
GasLimit::Fixed(ref limit) => *limit,
_ => !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 {
transaction_queue: txq,
next_allowed_reseal: Mutex::new(Instant::now()),
@ -1029,7 +1034,7 @@ impl MinerService for Miner {
mod tests {
use std::time::Duration;
use super::super::MinerService;
use super::super::{MinerService, PrioritizationStrategy};
use super::*;
use util::*;
use ethkey::{Generator, Random};
@ -1083,6 +1088,7 @@ mod tests {
tx_gas_limit: !U256::zero(),
tx_queue_size: 1024,
tx_queue_gas_limit: GasLimit::None,
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
pending_set: PendingSet::AlwaysSealing,
work_queue_size: 5,
enable_resubmission: true,

View File

@ -47,7 +47,7 @@ mod transaction_queue;
mod work_notify;
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::external::{ExternalMiner, ExternalMinerService};
pub use client::TransactionImportResult;

View File

@ -49,7 +49,7 @@
//! 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(st1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
//!
@ -130,11 +130,20 @@ struct TransactionOrder {
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
/// High nonce_height = Low priority (processed later)
nonce_height: U256,
/// Gas specified in the transaction.
gas: U256,
/// Gas Price of the transaction.
/// Low gas price = Low priority (processed later)
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: H256,
/// Origin of the transaction
@ -145,11 +154,15 @@ struct 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 {
nonce_height: tx.nonce() - base_nonce,
gas: tx.transaction.gas.clone(),
gas_price: tx.transaction.gas_price,
gas: tx.transaction.gas,
gas_factor: factor,
strategy: strategy,
hash: tx.hash(),
origin: tx.origin,
penalties: 0,
@ -197,11 +210,28 @@ impl Ord for TransactionOrder {
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
let a_gas = self.gas_price;
let b_gas = b.gas_price;
if a_gas != b_gas {
return b_gas.cmp(&a_gas);
if self.gas_price != b.gas_price {
return b.gas_price.cmp(&self.gas_price);
}
// 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.
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
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)
minimal_gas_price: U256,
/// The maximum amount of gas any individual transaction may use.
@ -435,18 +489,18 @@ pub struct TransactionQueue {
impl Default for TransactionQueue {
fn default() -> Self {
TransactionQueue::new()
TransactionQueue::new(PrioritizationStrategy::GasPriceOnly)
}
}
impl TransactionQueue {
/// Creates new instance of this Queue
pub fn new() -> Self {
Self::with_limits(1024, !U256::zero(), !U256::zero())
pub fn new(strategy: PrioritizationStrategy) -> Self {
Self::with_limits(strategy, 1024, !U256::zero(), !U256::zero())
}
/// 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 {
by_priority: BTreeSet::new(),
by_address: Table::new(),
@ -464,6 +518,7 @@ impl TransactionQueue {
};
TransactionQueue {
strategy: strategy,
minimal_gas_price: U256::zero(),
tx_gas_limit: tx_gas_limit,
gas_limit: !U256::zero(),
@ -844,6 +899,7 @@ impl TransactionQueue {
return Err(TransactionError::AlreadyImported);
}
let min_gas_price = (self.minimal_gas_price, self.strategy);
let address = tx.sender();
let nonce = tx.nonce();
let hash = tx.hash();
@ -881,7 +937,7 @@ impl TransactionQueue {
if nonce > next_nonce {
// We have a gap - put to future.
// 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
let removed = self.future.enforce_limit(&mut self.by_hash);
// 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);
// 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
let new_max = self.last_nonces.get(&address).map_or(nonce, |n| cmp::max(nonce, *n));
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
/// gas_price)
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
let order = TransactionOrder::for_transaction(&tx, base_nonce);
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, min_gas_price.0, min_gas_price.1);
let hash = tx.hash();
let address = tx.sender();
let nonce = tx.nonce();
@ -1015,12 +1071,12 @@ mod test {
fn default_gas_val() -> U256 { 100_000.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 {
action: Action::Create,
value: U256::from(100),
data: "3331600055".from_hex().unwrap(),
gas: default_gas_val(),
gas: gas,
gas_price: gas_price,
nonce: nonce
}
@ -1028,7 +1084,12 @@ mod test {
fn new_tx(nonce: U256, gas_price: U256) -> SignedTransaction {
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 {
@ -1043,8 +1104,8 @@ mod test {
}
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 tx2 = new_unsigned_tx(nonce + nonce_increment, gas_price + gas_price_increment);
let tx1 = new_unsigned_tx(nonce, default_gas_val(), gas_price);
let tx2 = new_unsigned_tx(nonce + nonce_increment, default_gas_val(), gas_price + gas_price_increment);
let keypair = Random.generate().unwrap();
let secret = &keypair.secret();
@ -1054,8 +1115,8 @@ mod test {
/// Returns two consecutive transactions, both with increased gas price
fn new_tx_pair_with_gas_price_increment(gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
let gas = default_gas_price() + gas_price_increment;
let tx1 = new_unsigned_tx(default_nonce(), gas);
let tx2 = new_unsigned_tx(default_nonce() + 1.into(), gas);
let tx1 = new_unsigned_tx(default_nonce(), default_gas_val(), gas);
let tx2 = new_unsigned_tx(default_nonce() + 1.into(), default_gas_val(), gas);
let keypair = Random.generate().unwrap();
let secret = &keypair.secret();
@ -1082,17 +1143,21 @@ mod test {
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]
fn should_return_correct_nonces_when_dropped_because_of_limit() {
// 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 sender = tx1.sender().unwrap();
let nonce = tx1.nonce;
txq.add(tx1.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.last_nonce(&sender), Some(nonce + U256::one()));
assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into()));
// when
let tx = new_tx(123.into(), 1.into());
@ -1138,9 +1203,9 @@ mod test {
x
};
// 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());
let order2 = TransactionOrder::for_transaction(&tx2, U256::zero());
let order2 = transaction_order(&tx2, U256::zero());
set.insert(tx2.sender(), tx2.nonce(), order2.clone());
assert_eq!(set.by_priority.len(), 2);
assert_eq!(set.by_address.len(), 2);
@ -1181,7 +1246,7 @@ mod test {
x
};
// 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());
assert_eq!(set.by_priority.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().1.len(), 1);
// 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());
assert_eq!(set.by_priority.len(), 1);
assert_eq!(set.by_address.len(), 1);
@ -1218,10 +1283,10 @@ mod test {
};
let tx = new_tx_default();
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());
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());
}
@ -1238,7 +1303,7 @@ mod test {
assert_eq!(set.gas_price_entry_limit(), 0.into());
let tx = new_tx_default();
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_eq!(set.gas_price_entry_limit(), 2.into());
}
@ -1246,7 +1311,7 @@ mod test {
#[test]
fn should_handle_same_transaction_imported_twice_with_different_state_nonces() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx, tx2) = new_similar_tx_pair();
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
!U256::zero() };
@ -1271,7 +1336,7 @@ mod test {
#[test]
fn should_move_all_transactions_from_future() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
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:
!U256::zero() };
@ -1297,7 +1362,7 @@ mod test {
#[test]
fn should_import_tx() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
// when
@ -1309,10 +1374,77 @@ mod test {
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]
fn gas_limit_should_never_overflow() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
txq.set_gas_limit(U256::zero());
assert_eq!(txq.gas_limit, U256::zero());
@ -1326,7 +1458,7 @@ mod test {
#[test]
fn should_not_import_transaction_above_gas_limit() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
let gas = tx.gas;
let limit = gas / U256::from(2);
@ -1349,7 +1481,7 @@ mod test {
#[test]
fn should_drop_transactions_from_senders_without_balance() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
let account = |a: &Address| AccountDetails {
nonce: default_account_details(a).nonce,
@ -1372,7 +1504,7 @@ mod test {
#[test]
fn should_not_import_transaction_below_min_gas_price_threshold_if_external() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
txq.set_minimal_gas_price(tx.gas_price + U256::one());
@ -1392,7 +1524,7 @@ mod test {
#[test]
fn should_import_transaction_below_min_gas_price_threshold_if_local() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
txq.set_minimal_gas_price(tx.gas_price + U256::one());
@ -1411,8 +1543,8 @@ mod test {
use rlp::{self, RlpStream, Stream};
// given
let mut txq = TransactionQueue::new();
let tx = new_unsigned_tx(123.into(), 1.into());
let mut txq = TransactionQueue::default();
let tx = new_unsigned_tx(123.into(), 100.into(), 1.into());
let stx = {
let mut s = RlpStream::new_list(9);
s.append(&tx.nonce);
@ -1436,7 +1568,7 @@ mod test {
#[test]
fn should_import_txs_from_same_sender() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
@ -1454,7 +1586,7 @@ mod test {
#[test]
fn should_prioritize_local_transactions_within_same_nonce_height() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
// the second one has same nonce but higher `gas_price`
let (_, tx2) = new_similar_tx_pair();
@ -1475,7 +1607,7 @@ mod test {
#[test]
fn should_prioritize_reimported_transactions_within_same_nonce_height() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
// the second one has same nonce but higher `gas_price`
let (_, tx2) = new_similar_tx_pair();
@ -1496,7 +1628,7 @@ mod test {
#[test]
fn should_not_prioritize_local_transactions_with_different_nonce_height() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
// when
@ -1514,7 +1646,7 @@ mod test {
fn should_penalize_transactions_from_sender_in_future() {
// given
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
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
@ -1543,7 +1675,7 @@ mod test {
#[test]
fn should_penalize_transactions_from_sender() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
// txa, txb - slightly bigger gas price to have consistent ordering
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
@ -1576,7 +1708,7 @@ mod test {
#[test]
fn should_return_pending_hashes() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
@ -1594,7 +1726,7 @@ mod test {
#[test]
fn should_put_transaction_to_futures_if_gap_detected() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(2.into(), 0.into());
@ -1620,7 +1752,7 @@ mod test {
!U256::zero() };
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());
txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap();
@ -1639,12 +1771,12 @@ mod test {
#[test]
fn should_move_transactions_if_gap_filled() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let kp = Random.generate().unwrap();
let secret = kp.secret();
let tx = new_unsigned_tx(123.into(), 1.into()).sign(secret);
let tx1 = new_unsigned_tx(124.into(), 1.into()).sign(secret);
let tx2 = new_unsigned_tx(125.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(), default_gas_val(), 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();
assert_eq!(txq.status().pending, 1);
@ -1666,7 +1798,7 @@ mod test {
#[test]
fn should_remove_transaction() {
// given
let mut txq2 = TransactionQueue::new();
let mut txq2 = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(3.into(), 0.into());
txq2.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq2.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
@ -1687,7 +1819,7 @@ mod test {
#[test]
fn should_move_transactions_to_future_if_gap_introduced() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
let tx3 = new_tx_default();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
@ -1708,7 +1840,7 @@ mod test {
#[test]
fn should_clear_queue() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
// add
@ -1728,7 +1860,7 @@ mod test {
#[test]
fn should_drop_old_transactions_when_hitting_the_limit() {
// 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 sender = tx.sender().unwrap();
let nonce = tx.nonce;
@ -1749,7 +1881,7 @@ mod test {
#[test]
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);
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
@ -1768,19 +1900,20 @@ mod test {
#[test]
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 (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).ok();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).ok();
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).ok();
txq.add(tx4.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).unwrap();
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap();
// limited by gas
txq.add(tx4.clone(), &default_account_details, TransactionOrigin::External).unwrap_err();
assert_eq!(txq.status().pending, 2);
}
#[test]
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 (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));
@ -1796,10 +1929,10 @@ mod test {
#[test]
fn should_drop_transactions_with_old_nonces() {
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
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
let res = txq.add(tx, &fetch_last_nonce, TransactionOrigin::External);
@ -1816,7 +1949,7 @@ mod test {
// given
let 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();
let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
assert_eq!(txq.status().future, 1);
@ -1835,7 +1968,7 @@ mod test {
#[test]
fn should_accept_same_transaction_twice_if_removed() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap();
@ -1856,7 +1989,7 @@ mod test {
#[test]
fn should_not_move_to_future_if_state_nonce_is_higher() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
let tx3 = new_tx_default();
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() {
init_log();
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
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 mut tx2 = (*tx).clone();
tx2.gas_price = U256::from(200);
@ -1902,9 +2035,9 @@ mod test {
#[test]
fn should_replace_same_transaction_when_importing_to_futures() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
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 mut tx1 = (*tx0).clone();
tx1.nonce = U256::from(124);
@ -1936,7 +2069,7 @@ mod test {
!U256::zero() };
let next_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();
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap();
txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap();
@ -1954,7 +2087,7 @@ mod test {
#[test]
fn should_return_none_when_transaction_from_given_address_does_not_exist() {
// given
let txq = TransactionQueue::new();
let txq = TransactionQueue::default();
// then
assert_eq!(txq.last_nonce(&Address::default()), None);
@ -1963,7 +2096,7 @@ mod test {
#[test]
fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let tx = new_tx_default();
let from = tx.sender().unwrap();
let nonce = tx.nonce;
@ -1979,7 +2112,7 @@ mod test {
#[test]
fn should_remove_old_transaction_even_if_newer_transaction_was_not_known() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
@ -1997,7 +2130,7 @@ mod test {
#[test]
fn should_return_valid_last_nonce_after_remove_all() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into());
let sender = tx1.sender().unwrap();
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
@ -2021,7 +2154,7 @@ mod test {
#[test]
fn should_return_true_if_there_is_local_transaction_pending() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
assert_eq!(txq.has_local_pending_transactions(), false);
@ -2037,7 +2170,7 @@ mod test {
#[test]
fn should_keep_right_order_in_future() {
// 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 prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
default_account_details(a).balance };
@ -2054,15 +2187,16 @@ mod test {
#[test]
fn should_return_correct_last_nonce() {
// given
let mut txq = TransactionQueue::new();
let mut txq = TransactionQueue::default();
let (tx1, tx2, tx2_2, tx3) = {
let keypair = Random.generate().unwrap();
let secret = &keypair.secret();
let nonce = 123.into();
let tx = new_unsigned_tx(nonce, 1.into());
let tx2 = new_unsigned_tx(nonce + 1.into(), 1.into());
let tx2_2 = new_unsigned_tx(nonce + 1.into(), 5.into());
let tx3 = new_unsigned_tx(nonce + 2.into(), 1.into());
let gas = default_gas_val();
let tx = new_unsigned_tx(nonce, gas, 1.into());
let tx2 = new_unsigned_tx(nonce + 1.into(), gas, 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))

View File

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

View File

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

View File

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

View File

@ -188,6 +188,12 @@ Sealing/Mining Options:
the queue. LIMIT can be either an amount of gas or
'auto' or 'off'. 'auto' sets the limit to be 20x
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
instead of cloning them. This gives a slightly
faster import speed, but means that extra solutions

View File

@ -30,7 +30,7 @@ use rpc::{IpcConfiguration, HttpConfiguration};
use ethcore_rpc::NetworkSettings;
use cache::CacheConfig;
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 ethcore_logger::Config as LogConfig;
use dir::Directories;
@ -360,6 +360,7 @@ impl Configuration {
},
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_strategy: try!(to_queue_strategy(&self.args.flag_tx_queue_strategy)),
pending_set: try!(to_pending_set(&self.args.flag_relay_set)),
reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period),
work_queue_size: self.args.flag_work_queue_size,
@ -647,6 +648,7 @@ mod tests {
use cli::Args;
use ethcore_rpc::NetworkSettings;
use ethcore::client::{VMType, BlockID};
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
use helpers::{replace_home, default_network_config};
use run::RunCmd;
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]
fn should_parse_network_settings() {
// given

View File

@ -22,7 +22,7 @@ use std::fs::File;
use util::{clean_0x, U256, Uint, Address, path, CompactionProfile};
use util::journaldb::Algorithm;
use ethcore::client::{Mode, BlockID, VMType, DatabaseCompactionProfile, ClientConfig};
use ethcore::miner::{PendingSet, GasLimit};
use ethcore::miner::{PendingSet, GasLimit, PrioritizationStrategy};
use cache::CacheConfig;
use dir::DatabaseDirectories;
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> {
match s {
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(),
gas_floor_target: U256::from(4_700_000),
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::views::BlockView;
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 devtools::RandomTempPath;
use util::Hashable;
@ -59,6 +59,7 @@ fn miner_service(spec: &Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
reseal_on_own_tx: true,
tx_queue_size: 1024,
tx_gas_limit: !U256::zero(),
tx_queue_strategy: PrioritizationStrategy::GasPriceOnly,
tx_queue_gas_limit: GasLimit::None,
pending_set: PendingSet::SealingOrElseQueue,
reseal_min_period: Duration::from_secs(0),