Fixing removal from gas price when moving future->current (#2076)
* Fixing removal from gas price when moving future->current * unwrap -> expect
This commit is contained in:
parent
341e06481e
commit
325967cadb
@ -81,6 +81,7 @@
|
|||||||
//! - It removes all transactions (either from `current` or `future`) with nonce < client nonce
|
//! - It removes all transactions (either from `current` or `future`) with nonce < client nonce
|
||||||
//! - It moves matching `future` transactions to `current`
|
//! - It moves matching `future` transactions to `current`
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::{HashSet, HashMap, BTreeSet, BTreeMap};
|
use std::collections::{HashSet, HashMap, BTreeSet, BTreeMap};
|
||||||
@ -215,7 +216,48 @@ impl VerifiedTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sender(&self) -> Address {
|
fn sender(&self) -> Address {
|
||||||
self.transaction.sender().unwrap()
|
self.transaction.sender().expect("Sender is verified in new; qed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct GasPriceQueue {
|
||||||
|
backing: BTreeMap<U256, HashSet<H256>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GasPriceQueue {
|
||||||
|
/// Insert an item into a BTreeMap/HashSet "multimap".
|
||||||
|
pub fn insert(&mut self, gas_price: U256, hash: H256) -> bool {
|
||||||
|
self.backing.entry(gas_price).or_insert_with(Default::default).insert(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove an item from a BTreeMap/HashSet "multimap".
|
||||||
|
/// Returns true if the item was removed successfully.
|
||||||
|
pub fn remove(&mut self, gas_price: &U256, hash: &H256) -> bool {
|
||||||
|
if let Some(mut hashes) = self.backing.get_mut(gas_price) {
|
||||||
|
let only_one_left = hashes.len() == 1;
|
||||||
|
if !only_one_left {
|
||||||
|
// Operation may be ok: only if hash is in gas-price's Set.
|
||||||
|
return hashes.remove(hash);
|
||||||
|
}
|
||||||
|
if hash != hashes.iter().next().expect("We know there is only one element in collection, tested above; qed") {
|
||||||
|
// Operation failed: hash not the single item in gas-price's Set.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Operation failed: gas-price not found in Map.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Operation maybe ok: only if hash not found in gas-price Set.
|
||||||
|
self.backing.remove(gas_price).is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for GasPriceQueue {
|
||||||
|
type Target=BTreeMap<U256, HashSet<H256>>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.backing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +269,7 @@ impl VerifiedTransaction {
|
|||||||
struct TransactionSet {
|
struct TransactionSet {
|
||||||
by_priority: BTreeSet<TransactionOrder>,
|
by_priority: BTreeSet<TransactionOrder>,
|
||||||
by_address: Table<Address, U256, TransactionOrder>,
|
by_address: Table<Address, U256, TransactionOrder>,
|
||||||
by_gas_price: BTreeMap<U256, HashSet<H256>>,
|
by_gas_price: GasPriceQueue,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,12 +287,12 @@ impl TransactionSet {
|
|||||||
// If transaction was replaced remove it from priority queue
|
// If transaction was replaced remove it from priority queue
|
||||||
if let Some(ref old_order) = by_address_replaced {
|
if let Some(ref old_order) = by_address_replaced {
|
||||||
assert!(self.by_priority.remove(old_order), "hash is in `by_address`; all transactions in `by_address` must be in `by_priority`; qed");
|
assert!(self.by_priority.remove(old_order), "hash is in `by_address`; all transactions in `by_address` must be in `by_priority`; qed");
|
||||||
assert!(Self::remove_item(&mut self.by_gas_price, &old_order.gas_price, &old_order.hash),
|
assert!(self.by_gas_price.remove(&old_order.gas_price, &old_order.hash),
|
||||||
"hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_gas_limit`; qed");
|
"hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_gas_limit`; qed");
|
||||||
}
|
}
|
||||||
Self::insert_item(&mut self.by_gas_price, order_gas_price, order_hash);
|
self.by_gas_price.insert(order_gas_price, order_hash);
|
||||||
debug_assert_eq!(self.by_priority.len(), self.by_address.len());
|
assert_eq!(self.by_priority.len(), self.by_address.len());
|
||||||
debug_assert_eq!(self.by_gas_price.iter().map(|(_, v)| v.len()).fold(0, |a, b| a + b), self.by_address.len());
|
assert_eq!(self.by_gas_price.values().map(|v| v.len()).fold(0, |a, b| a + b), self.by_address.len());
|
||||||
by_address_replaced
|
by_address_replaced
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,6 +305,7 @@ impl TransactionSet {
|
|||||||
if len <= self.limit {
|
if len <= self.limit {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let to_drop : Vec<(Address, U256)> = {
|
let to_drop : Vec<(Address, U256)> = {
|
||||||
self.by_priority
|
self.by_priority
|
||||||
.iter()
|
.iter()
|
||||||
@ -290,13 +333,16 @@ impl TransactionSet {
|
|||||||
/// Drop transaction from this set (remove from `by_priority` and `by_address`)
|
/// Drop transaction from this set (remove from `by_priority` and `by_address`)
|
||||||
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
|
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
|
||||||
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
|
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
|
||||||
assert!(Self::remove_item(&mut self.by_gas_price, &tx_order.gas_price, &tx_order.hash),
|
assert!(self.by_gas_price.remove(&tx_order.gas_price, &tx_order.hash),
|
||||||
"hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_gas_limit`; qed");
|
"hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_gas_limit`; qed");
|
||||||
self.by_priority.remove(&tx_order);
|
assert!(self.by_priority.remove(&tx_order),
|
||||||
|
"hash is in `by_address`; all transactions' gas_prices in `by_address` must be in `by_priority`; qed");
|
||||||
assert_eq!(self.by_priority.len(), self.by_address.len());
|
assert_eq!(self.by_priority.len(), self.by_address.len());
|
||||||
|
assert_eq!(self.by_gas_price.values().map(|v| v.len()).fold(0, |a, b| a + b), self.by_address.len());
|
||||||
return Some(tx_order);
|
return Some(tx_order);
|
||||||
}
|
}
|
||||||
assert_eq!(self.by_priority.len(), self.by_address.len());
|
assert_eq!(self.by_priority.len(), self.by_address.len());
|
||||||
|
assert_eq!(self.by_gas_price.values().map(|v| v.len()).fold(0, |a, b| a + b), self.by_address.len());
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +350,7 @@ impl TransactionSet {
|
|||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
self.by_priority.clear();
|
self.by_priority.clear();
|
||||||
self.by_address.clear();
|
self.by_address.clear();
|
||||||
self.by_gas_price.clear();
|
self.by_gas_price.backing.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets new limit for number of transactions in this `TransactionSet`.
|
/// Sets new limit for number of transactions in this `TransactionSet`.
|
||||||
@ -321,32 +367,6 @@ impl TransactionSet {
|
|||||||
_ => U256::default(),
|
_ => U256::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert an item into a BTreeMap/HashSet "multimap".
|
|
||||||
fn insert_item(into: &mut BTreeMap<U256, HashSet<H256>>, gas_price: U256, hash: H256) -> bool {
|
|
||||||
into.entry(gas_price).or_insert_with(Default::default).insert(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove an item from a BTreeMap/HashSet "multimap".
|
|
||||||
/// Returns true if the item was removed successfully.
|
|
||||||
fn remove_item(from: &mut BTreeMap<U256, HashSet<H256>>, gas_price: &U256, hash: &H256) -> bool {
|
|
||||||
if let Some(mut hashes) = from.get_mut(gas_price) {
|
|
||||||
let only_one_left = hashes.len() == 1;
|
|
||||||
if !only_one_left {
|
|
||||||
// Operation may be ok: only if hash is in gas-price's Set.
|
|
||||||
return hashes.remove(hash);
|
|
||||||
}
|
|
||||||
if hashes.iter().next().unwrap() != hash {
|
|
||||||
// Operation failed: hash not the single item in gas-price's Set.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Operation failed: gas-price not found in Map.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Operation maybe ok: only if hash not found in gas-price Set.
|
|
||||||
from.remove(gas_price).is_some()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -588,7 +608,7 @@ impl TransactionQueue {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let transaction = transaction.unwrap();
|
let transaction = transaction.expect("None is tested in early-exit condition above; qed");
|
||||||
let sender = transaction.sender();
|
let sender = transaction.sender();
|
||||||
let nonce = transaction.nonce();
|
let nonce = transaction.nonce();
|
||||||
let current_nonce = fetch_account(&sender).nonce;
|
let current_nonce = fetch_account(&sender).nonce;
|
||||||
@ -623,7 +643,7 @@ impl TransactionQueue {
|
|||||||
None => vec![],
|
None => vec![],
|
||||||
};
|
};
|
||||||
for k in all_nonces_from_sender {
|
for k in all_nonces_from_sender {
|
||||||
let order = self.future.drop(sender, &k).unwrap();
|
let order = self.future.drop(sender, &k).expect("iterating over a collection that has been retrieved above; qed");
|
||||||
if k >= current_nonce {
|
if k >= current_nonce {
|
||||||
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
||||||
} else {
|
} else {
|
||||||
@ -644,7 +664,8 @@ impl TransactionQueue {
|
|||||||
|
|
||||||
for k in all_nonces_from_sender {
|
for k in all_nonces_from_sender {
|
||||||
// Goes to future or is removed
|
// Goes to future or is removed
|
||||||
let order = self.current.drop(sender, &k).unwrap();
|
let order = self.current.drop(sender, &k).expect("iterating over a collection that has been retrieved above;
|
||||||
|
qed");
|
||||||
if k >= current_nonce {
|
if k >= current_nonce {
|
||||||
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
self.future.insert(*sender, k, order.update_height(k, current_nonce));
|
||||||
} else {
|
} else {
|
||||||
@ -704,10 +725,11 @@ impl TransactionQueue {
|
|||||||
if let None = by_nonce {
|
if let None = by_nonce {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut by_nonce = by_nonce.unwrap();
|
let mut by_nonce = by_nonce.expect("None is tested in early-exit condition above; qed");
|
||||||
while let Some(order) = by_nonce.remove(¤t_nonce) {
|
while let Some(order) = by_nonce.remove(¤t_nonce) {
|
||||||
// remove also from priority and hash
|
// remove also from priority and gas_price
|
||||||
self.future.by_priority.remove(&order);
|
self.future.by_priority.remove(&order);
|
||||||
|
self.future.by_gas_price.remove(&order.gas_price, &order.hash);
|
||||||
// Put to current
|
// Put to current
|
||||||
let order = order.update_height(current_nonce, first_nonce);
|
let order = order.update_height(current_nonce, first_nonce);
|
||||||
self.current.insert(address, current_nonce, order);
|
self.current.insert(address, current_nonce, order);
|
||||||
@ -1395,6 +1417,9 @@ mod test {
|
|||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 3);
|
assert_eq!(stats.pending, 3);
|
||||||
assert_eq!(stats.future, 0);
|
assert_eq!(stats.future, 0);
|
||||||
|
assert_eq!(txq.future.by_priority.len(), 0);
|
||||||
|
assert_eq!(txq.future.by_address.len(), 0);
|
||||||
|
assert_eq!(txq.future.by_gas_price.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user