// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . use std::{fmt, mem}; use log::warn; use smallvec::SmallVec; use crate::{ pool::Transaction, ready::{Readiness, Ready}, scoring::{self, Scoring}, }; #[derive(Debug)] pub enum AddResult { Ok(T), TooCheapToEnter(T, S), TooCheap { old: T, new: T }, Replaced { old: T, new: T }, PushedOut { old: T, new: T }, } /// Represents all transactions from a particular sender ordered by nonce. const PER_SENDER: usize = 8; #[derive(Debug)] pub struct Transactions> { // TODO [ToDr] Consider using something that doesn't require shifting all records. transactions: SmallVec<[Transaction; PER_SENDER]>, scores: SmallVec<[S::Score; PER_SENDER]>, } impl> Default for Transactions { fn default() -> Self { Transactions { transactions: Default::default(), scores: Default::default(), } } } impl> Transactions { pub fn is_empty(&self) -> bool { self.transactions.is_empty() } pub fn len(&self) -> usize { self.transactions.len() } pub fn iter_transactions(&self) -> ::std::slice::Iter> { self.transactions.iter() } pub fn iter_scores(&self) -> ::std::slice::Iter { self.scores.iter() } pub fn worst_and_best( &self, ) -> Option<((S::Score, Transaction), (S::Score, Transaction))> { let len = self.scores.len(); self.scores.get(0).cloned().map(|best| { let worst = self.scores[len - 1].clone(); let best_tx = self.transactions[0].clone(); let worst_tx = self.transactions[len - 1].clone(); ((worst, worst_tx), (best, best_tx)) }) } pub fn find_next(&self, tx: &T, scoring: &S) -> Option<(S::Score, Transaction)> { self.transactions .binary_search_by(|old| scoring.compare(old, &tx)) .ok() .and_then(|index| { let index = index + 1; if index < self.scores.len() { Some((self.scores[index].clone(), self.transactions[index].clone())) } else { None } }) } fn push_cheapest_transaction( &mut self, tx: Transaction, scoring: &S, max_count: usize, ) -> AddResult, S::Score> { let index = self.transactions.len(); if index == max_count && !scoring.should_ignore_sender_limit(&tx) { let min_score = self.scores[index - 1].clone(); AddResult::TooCheapToEnter(tx, min_score) } else { self.transactions.push(tx.clone()); self.scores.push(Default::default()); scoring.update_scores( &self.transactions, &mut self.scores, scoring::Change::InsertedAt(index), ); AddResult::Ok(tx) } } pub fn update_scores(&mut self, scoring: &S, event: S::Event) { scoring.update_scores( &self.transactions, &mut self.scores, scoring::Change::Event(event), ); } pub fn add( &mut self, new: Transaction, scoring: &S, max_count: usize, ) -> AddResult, S::Score> { let index = match self .transactions .binary_search_by(|old| scoring.compare(old, &new)) { Ok(index) => index, Err(index) => index, }; // Insert at the end. if index == self.transactions.len() { return self.push_cheapest_transaction(new, scoring, max_count); } // Decide if the transaction should replace some other. match scoring.choose(&self.transactions[index], &new) { // New transaction should be rejected scoring::Choice::RejectNew => AddResult::TooCheap { old: self.transactions[index].clone(), new, }, // New transaction should be kept along with old ones. scoring::Choice::InsertNew => { self.transactions.insert(index, new.clone()); self.scores.insert(index, Default::default()); scoring.update_scores( &self.transactions, &mut self.scores, scoring::Change::InsertedAt(index), ); if self.transactions.len() > max_count { let old = self.transactions.pop().expect("len is non-zero"); self.scores.pop(); scoring.update_scores( &self.transactions, &mut self.scores, scoring::Change::RemovedAt(self.transactions.len()), ); AddResult::PushedOut { old, new } } else { AddResult::Ok(new) } } // New transaction is replacing some other transaction already in the queue. scoring::Choice::ReplaceOld => { let old = mem::replace(&mut self.transactions[index], new.clone()); scoring.update_scores( &self.transactions, &mut self.scores, scoring::Change::ReplacedAt(index), ); AddResult::Replaced { old, new } } } } pub fn remove(&mut self, tx: &T, scoring: &S) -> bool { let index = match self .transactions .binary_search_by(|old| scoring.compare(old, tx)) { Ok(index) => index, Err(_) => { warn!("Attempting to remove non-existent transaction {:?}", tx); return false; } }; self.transactions.remove(index); self.scores.remove(index); // Update scoring scoring.update_scores( &self.transactions, &mut self.scores, scoring::Change::RemovedAt(index), ); return true; } pub fn cull>( &mut self, ready: &mut R, scoring: &S, ) -> SmallVec<[Transaction; PER_SENDER]> { let mut result = SmallVec::new(); if self.is_empty() { return result; } let mut first_non_stalled = 0; for tx in &self.transactions { match ready.is_ready(tx) { Readiness::Stale => { first_non_stalled += 1; } Readiness::Ready | Readiness::Future => break, } } if first_non_stalled == 0 { return result; } // reverse the vectors to easily remove first elements. self.transactions.reverse(); self.scores.reverse(); for _ in 0..first_non_stalled { self.scores.pop(); result.push( self.transactions .pop() .expect("first_non_stalled is never greater than transactions.len(); qed"), ); } self.transactions.reverse(); self.scores.reverse(); // update scoring scoring.update_scores( &self.transactions, &mut self.scores, scoring::Change::Culled(result.len()), ); // reverse the result to maintain correct order. result.reverse(); result } }