use std::cmp;
use vapory_types::U256;
use txpool::{self, scoring};
use super::{verifier, PrioritizationStrategy, VerifiedTransaction, ScoredTransaction};
const GAS_PRICE_BUMP_SHIFT: usize = 3;
#[inline]
fn bump_gas_price(old_gp: U256) -> U256 {
old_gp.saturating_add(old_gp >> GAS_PRICE_BUMP_SHIFT)
}
#[derive(Debug, Clone)]
pub struct NonceAndGasPrice(pub PrioritizationStrategy);
impl NonceAndGasPrice {
pub fn should_reject_early(&self, old: &VerifiedTransaction, new: &verifier::Transaction) -> bool {
if old.priority().is_local() {
return true
}
&old.transaction.gas_price > new.gas_price()
}
}
impl<P> txpool::Scoring<P> for NonceAndGasPrice where P: ScoredTransaction + txpool::VerifiedTransaction {
type Score = U256;
type Event = ();
fn compare(&self, old: &P, other: &P) -> cmp::Ordering {
old.nonce().cmp(&other.nonce())
}
fn choose(&self, old: &P, new: &P) -> scoring::Choice {
if old.nonce() != new.nonce() {
return scoring::Choice::InsertNew
}
let old_gp = old.gas_price();
let new_gp = new.gas_price();
let min_required_gp = bump_gas_price(*old_gp);
match min_required_gp.cmp(&new_gp) {
cmp::Ordering::Greater => scoring::Choice::RejectNew,
_ => scoring::Choice::ReplaceOld,
}
}
fn update_scores(&self, txs: &[txpool::Transaction<P>], scores: &mut [U256], change: scoring::Change) {
use self::scoring::Change;
match change {
Change::Culled(_) => {},
Change::RemovedAt(_) => {}
Change::InsertedAt(i) | Change::ReplacedAt(i) => {
assert!(i < txs.len());
assert!(i < scores.len());
scores[i] = *txs[i].transaction.gas_price();
let boost = match txs[i].priority() {
super::Priority::Local => 15,
super::Priority::Retracted => 10,
super::Priority::Regular => 0,
};
scores[i] = scores[i] << boost;
},
Change::Event(_) => {
for (score, tx) in scores.iter_mut().zip(txs) {
if !tx.priority().is_local() {
*score = *score >> 3;
}
}
},
}
}
fn should_ignore_sender_limit(&self, new: &P) -> bool {
new.priority().is_local()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use pool::tests::tx::{Tx, TxExt};
use txpool::Scoring;
#[test]
fn should_calculate_score_correctly() {
let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly);
let (tx1, tx2, tx3) = Tx::default().signed_triple();
let transactions = vec![tx1, tx2, tx3].into_iter().enumerate().map(|(i, tx)| {
let mut verified = tx.verified();
verified.priority = match i {
0 => ::pool::Priority::Local,
1 => ::pool::Priority::Retracted,
_ => ::pool::Priority::Regular,
};
txpool::Transaction {
insertion_id: 0,
transaction: Arc::new(verified),
}
}).collect::<Vec<_>>();
let initial_scores = vec![U256::from(0), 0.into(), 0.into()];
let mut scores = initial_scores.clone();
scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(0));
scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(1));
scoring.update_scores(&transactions, &mut *scores, scoring::Change::Culled(2));
assert_eq!(scores, initial_scores);
let mut scores = initial_scores.clone();
scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(0));
scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(1));
scoring.update_scores(&transactions, &mut *scores, scoring::Change::RemovedAt(2));
assert_eq!(scores, initial_scores);
let mut scores = initial_scores.clone();
scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(0));
assert_eq!(scores, vec![32768.into(), 0.into(), 0.into()]);
scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(1));
assert_eq!(scores, vec![32768.into(), 1024.into(), 0.into()]);
scoring.update_scores(&transactions, &mut *scores, scoring::Change::InsertedAt(2));
assert_eq!(scores, vec![32768.into(), 1024.into(), 1.into()]);
let mut scores = initial_scores.clone();
scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(0));
assert_eq!(scores, vec![32768.into(), 0.into(), 0.into()]);
scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(1));
assert_eq!(scores, vec![32768.into(), 1024.into(), 0.into()]);
scoring.update_scores(&transactions, &mut *scores, scoring::Change::ReplacedAt(2));
assert_eq!(scores, vec![32768.into(), 1024.into(), 1.into()]);
scoring.update_scores(&transactions, &mut *scores, scoring::Change::Event(()));
assert_eq!(scores, vec![32768.into(), 128.into(), 0.into()]);
}
}