|
|
|
@@ -130,6 +130,8 @@ 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,
|
|
|
|
@@ -146,6 +148,7 @@ impl TransactionOrder {
|
|
|
|
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
|
|
|
fn for_transaction(tx: &VerifiedTransaction, base_nonce: U256) -> Self {
|
|
|
|
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,
|
|
|
|
hash: tx.hash(),
|
|
|
|
hash: tx.hash(),
|
|
|
|
origin: tx.origin,
|
|
|
|
origin: tx.origin,
|
|
|
|
@@ -287,6 +290,7 @@ struct TransactionSet {
|
|
|
|
by_address: Table<Address, U256, TransactionOrder>,
|
|
|
|
by_address: Table<Address, U256, TransactionOrder>,
|
|
|
|
by_gas_price: GasPriceQueue,
|
|
|
|
by_gas_price: GasPriceQueue,
|
|
|
|
limit: usize,
|
|
|
|
limit: usize,
|
|
|
|
|
|
|
|
gas_limit: U256,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl TransactionSet {
|
|
|
|
impl TransactionSet {
|
|
|
|
@@ -317,15 +321,18 @@ impl TransactionSet {
|
|
|
|
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
|
|
|
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
|
|
|
/// Returns addresses and lowest nonces of transactions removed because of limit.
|
|
|
|
/// Returns addresses and lowest nonces of transactions removed because of limit.
|
|
|
|
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> Option<HashMap<Address, U256>> {
|
|
|
|
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> Option<HashMap<Address, U256>> {
|
|
|
|
let len = self.by_priority.len();
|
|
|
|
let mut count = 0;
|
|
|
|
if len <= self.limit {
|
|
|
|
let mut gas: U256 = 0.into();
|
|
|
|
return None;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let to_drop : Vec<(Address, U256)> = {
|
|
|
|
let to_drop : Vec<(Address, U256)> = {
|
|
|
|
self.by_priority
|
|
|
|
self.by_priority
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.skip(self.limit)
|
|
|
|
.skip_while(|order| {
|
|
|
|
|
|
|
|
count = count + 1;
|
|
|
|
|
|
|
|
let r = gas.overflowing_add(order.gas);
|
|
|
|
|
|
|
|
if r.1 { return false }
|
|
|
|
|
|
|
|
gas = r.0;
|
|
|
|
|
|
|
|
count <= self.limit && gas <= self.gas_limit
|
|
|
|
|
|
|
|
})
|
|
|
|
.map(|order| by_hash.get(&order.hash)
|
|
|
|
.map(|order| by_hash.get(&order.hash)
|
|
|
|
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
|
|
|
|
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
|
|
|
|
.map(|tx| (tx.sender(), tx.nonce()))
|
|
|
|
.map(|tx| (tx.sender(), tx.nonce()))
|
|
|
|
@@ -432,16 +439,17 @@ impl Default for TransactionQueue {
|
|
|
|
impl TransactionQueue {
|
|
|
|
impl TransactionQueue {
|
|
|
|
/// Creates new instance of this Queue
|
|
|
|
/// Creates new instance of this Queue
|
|
|
|
pub fn new() -> Self {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self::with_limits(1024, !U256::zero())
|
|
|
|
Self::with_limits(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, tx_gas_limit: U256) -> Self {
|
|
|
|
pub fn with_limits(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(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
limit: limit,
|
|
|
|
limit: limit,
|
|
|
|
|
|
|
|
gas_limit: gas_limit,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let future = TransactionSet {
|
|
|
|
let future = TransactionSet {
|
|
|
|
@@ -449,6 +457,7 @@ impl TransactionQueue {
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
limit: limit,
|
|
|
|
limit: limit,
|
|
|
|
|
|
|
|
gas_limit: gas_limit,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
TransactionQueue {
|
|
|
|
TransactionQueue {
|
|
|
|
@@ -504,6 +513,13 @@ impl TransactionQueue {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Sets new total gas limit.
|
|
|
|
|
|
|
|
pub fn set_total_gas_limit(&mut self, gas_limit: U256) {
|
|
|
|
|
|
|
|
self.future.gas_limit = gas_limit;
|
|
|
|
|
|
|
|
self.current.gas_limit = gas_limit;
|
|
|
|
|
|
|
|
self.future.enforce_limit(&mut self.by_hash);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Set the new limit for the amount of gas any individual transaction may have.
|
|
|
|
/// Set the new limit for the amount of gas any individual transaction may have.
|
|
|
|
/// Any transaction already imported to the queue is not affected.
|
|
|
|
/// Any transaction already imported to the queue is not affected.
|
|
|
|
pub fn set_tx_gas_limit(&mut self, limit: U256) {
|
|
|
|
pub fn set_tx_gas_limit(&mut self, limit: U256) {
|
|
|
|
@@ -827,6 +843,16 @@ impl TransactionQueue {
|
|
|
|
let nonce = tx.nonce();
|
|
|
|
let nonce = tx.nonce();
|
|
|
|
let hash = tx.hash();
|
|
|
|
let hash = tx.hash();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// Rough size sanity check
|
|
|
|
|
|
|
|
let gas = &tx.transaction.gas;
|
|
|
|
|
|
|
|
if U256::from(tx.transaction.data.len()) > *gas {
|
|
|
|
|
|
|
|
// Droping transaction
|
|
|
|
|
|
|
|
trace!(target: "txqueue", "Dropping oversized transaction: {:?} (gas: {} < size {})", hash, gas, tx.transaction.data.len());
|
|
|
|
|
|
|
|
return Err(TransactionError::LimitReached);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The transaction might be old, let's check that.
|
|
|
|
// The transaction might be old, let's check that.
|
|
|
|
// This has to be the first test, otherwise calculating
|
|
|
|
// This has to be the first test, otherwise calculating
|
|
|
|
// nonce height would result in overflow.
|
|
|
|
// nonce height would result in overflow.
|
|
|
|
@@ -979,6 +1005,7 @@ mod test {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn default_nonce() -> U256 { 123.into() }
|
|
|
|
fn default_nonce() -> U256 { 123.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_price: U256) -> Transaction {
|
|
|
|
@@ -986,7 +1013,7 @@ mod test {
|
|
|
|
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: U256::from(100_000),
|
|
|
|
gas: default_gas_val(),
|
|
|
|
gas_price: gas_price,
|
|
|
|
gas_price: gas_price,
|
|
|
|
nonce: nonce
|
|
|
|
nonce: nonce
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1051,7 +1078,7 @@ mod test {
|
|
|
|
#[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());
|
|
|
|
let mut txq = TransactionQueue::with_limits(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;
|
|
|
|
@@ -1089,7 +1116,8 @@ mod test {
|
|
|
|
by_priority: BTreeSet::new(),
|
|
|
|
by_priority: BTreeSet::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
limit: 1
|
|
|
|
limit: 1,
|
|
|
|
|
|
|
|
gas_limit: !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 tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External).unwrap();
|
|
|
|
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External).unwrap();
|
|
|
|
@@ -1129,7 +1157,8 @@ mod test {
|
|
|
|
by_priority: BTreeSet::new(),
|
|
|
|
by_priority: BTreeSet::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
limit: 1
|
|
|
|
limit: 1,
|
|
|
|
|
|
|
|
gas_limit: !U256::zero(),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
// Create two transactions with same nonce
|
|
|
|
// Create two transactions with same nonce
|
|
|
|
// (same hash)
|
|
|
|
// (same hash)
|
|
|
|
@@ -1177,7 +1206,8 @@ mod test {
|
|
|
|
by_priority: BTreeSet::new(),
|
|
|
|
by_priority: BTreeSet::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
limit: 2
|
|
|
|
limit: 2,
|
|
|
|
|
|
|
|
gas_limit: !U256::zero(),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
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();
|
|
|
|
@@ -1194,7 +1224,8 @@ mod test {
|
|
|
|
by_priority: BTreeSet::new(),
|
|
|
|
by_priority: BTreeSet::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_address: Table::new(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
by_gas_price: Default::default(),
|
|
|
|
limit: 1
|
|
|
|
limit: 1,
|
|
|
|
|
|
|
|
gas_limit: !U256::zero(),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
|
|
|
assert_eq!(set.gas_price_entry_limit(), 0.into());
|
|
|
|
@@ -1690,7 +1721,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());
|
|
|
|
let mut txq = TransactionQueue::with_limits(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;
|
|
|
|
@@ -1711,7 +1742,7 @@ mod test {
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
fn should_limit_future_transactions() {
|
|
|
|
fn should_limit_future_transactions() {
|
|
|
|
let mut txq = TransactionQueue::with_limits(1, !U256::zero());
|
|
|
|
let mut txq = TransactionQueue::with_limits(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());
|
|
|
|
@@ -1728,6 +1759,16 @@ mod test {
|
|
|
|
assert_eq!(txq.status().future, 1);
|
|
|
|
assert_eq!(txq.status().future, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn should_limit_by_gas() {
|
|
|
|
|
|
|
|
let mut txq = TransactionQueue::with_limits(100, default_gas_val() * U256::from(2), !U256::zero());
|
|
|
|
|
|
|
|
let (tx1, _) = new_tx_pair_default(U256::from(4), U256::from(1));
|
|
|
|
|
|
|
|
let (tx3, _) = new_tx_pair_default(U256::from(4), U256::from(2));
|
|
|
|
|
|
|
|
txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
|
|
|
|
|
|
|
txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(txq.status().pending, 2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[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::new();
|
|
|
|
@@ -1971,7 +2012,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());
|
|
|
|
let mut txq = TransactionQueue::with_limits(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 };
|
|
|
|
|