Recover transaction pool on light client (#3833)

* recover tx pool on light client

* revert local tests fix

* removed import renamings

* futures03::Future -> std::future::Future

* Update core/transaction-pool/graph/src/error.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* replace remove_from_ready with remove_invalid

* avoid excess hashing

* debug -> warn

* TransactionPool + BasicTransactionPool

* pause future tx reject when resubmitting

* bump impl_version to make CI happy

* and revert back local test fixes

* alter doc to restart CI

* Transaction::clone() -> Transaction::duplicate()

* transactions -> updated_tranasctions

* remove explicit consensus-common ref

* ::std:: -> std::

* manual set/unset flag -> calling clusore with given flag value

* removed comments

* removed force argument

* BestIterator -> Box<Iterator>

* separate crate for TxPool + Maintainer trait

* long line fix

* pos-merge fix

* fix benches compilation

* Rename txpoolapi to txpool_api

* Clean up.

* Finalize merge.

* post-merge fix

* Move transaction pool api to primitives directly.

* Consistent naming for txpool-runtime-api

* Warn about missing docs.

* Move  abstraction for offchain calls to tx-pool-api.

* Merge RPC instantiation.

* Update cargo.lock

* Post merge fixes.

* Avoid depending on client.

* Fix build
This commit is contained in:
Svyatoslav Nikolsky
2019-11-28 03:00:54 +03:00
committed by Gavin Wood
parent 3e26fceda4
commit a782021ee8
64 changed files with 2370 additions and 667 deletions
@@ -34,8 +34,8 @@ use sr_primitives::transaction_validity::{
TransactionLongevity as Longevity,
TransactionPriority as Priority,
};
use txpool_api::{error, PoolStatus, InPoolTransaction};
use crate::error;
use crate::future::{FutureTransactions, WaitingTransaction};
use crate::ready::ReadyTransactions;
@@ -104,13 +104,65 @@ pub struct Transaction<Hash, Extrinsic> {
pub propagate: bool,
}
impl<Hash, Extrinsic> Transaction<Hash, Extrinsic> {
/// Returns `true` if the transaction should be propagated to other peers.
pub fn is_propagateable(&self) -> bool {
impl<Hash, Extrinsic> AsRef<Extrinsic> for Transaction<Hash, Extrinsic> {
fn as_ref(&self) -> &Extrinsic {
&self.data
}
}
impl<Hash, Extrinsic> InPoolTransaction for Transaction<Hash, Extrinsic> {
type Transaction = Extrinsic;
type Hash = Hash;
fn data(&self) -> &Extrinsic {
&self.data
}
fn hash(&self) -> &Hash {
&self.hash
}
fn priority(&self) -> &Priority {
&self.priority
}
fn longevity(&self) ->&Longevity {
&self.valid_till
}
fn requires(&self) -> &[Tag] {
&self.requires
}
fn provides(&self) -> &[Tag] {
&self.provides
}
fn is_propagateable(&self) -> bool {
self.propagate
}
}
impl<Hash: Clone, Extrinsic: Clone> Transaction<Hash, Extrinsic> {
/// Explicit transaction clone.
///
/// Transaction should be cloned only if absolutely necessary && we want
/// every reason to be commented. That's why we `Transaction` is not `Clone`,
/// but there's explicit `duplicate` method.
pub fn duplicate(&self) -> Self {
Transaction {
data: self.data.clone(),
bytes: self.bytes.clone(),
hash: self.hash.clone(),
priority: self.priority.clone(),
valid_till: self.valid_till.clone(),
requires: self.requires.clone(),
provides: self.provides.clone(),
propagate: self.propagate,
}
}
}
impl<Hash, Extrinsic> fmt::Debug for Transaction<Hash, Extrinsic> where
Hash: fmt::Debug,
Extrinsic: fmt::Debug,
@@ -159,6 +211,7 @@ const RECENTLY_PRUNED_TAGS: usize = 2;
/// required tags.
#[derive(Debug)]
pub struct BasePool<Hash: hash::Hash + Eq, Ex> {
reject_future_transactions: bool,
future: FutureTransactions<Hash, Ex>,
ready: ReadyTransactions<Hash, Ex>,
/// Store recently pruned tags (for last two invocations).
@@ -169,18 +222,37 @@ pub struct BasePool<Hash: hash::Hash + Eq, Ex> {
recently_pruned_index: usize,
}
impl<Hash: hash::Hash + Eq, Ex> Default for BasePool<Hash, Ex> {
impl<Hash: hash::Hash + Member + Serialize, Ex: std::fmt::Debug> Default for BasePool<Hash, Ex> {
fn default() -> Self {
Self::new(false)
}
}
impl<Hash: hash::Hash + Member + Serialize, Ex: std::fmt::Debug> BasePool<Hash, Ex> {
/// Create new pool given reject_future_transactions flag.
pub fn new(reject_future_transactions: bool) -> Self {
BasePool {
reject_future_transactions,
future: Default::default(),
ready: Default::default(),
recently_pruned: Default::default(),
recently_pruned_index: 0,
}
}
}
impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash, Ex> {
/// Temporary enables future transactions, runs closure and then restores
/// `reject_future_transactions` flag back to previous value.
///
/// The closure accepts the mutable reference to the pool and original value
/// of the `reject_future_transactions` flag.
pub(crate) fn with_futures_enabled<T>(&mut self, closure: impl FnOnce(&mut Self, bool) -> T) -> T {
let previous = self.reject_future_transactions;
self.reject_future_transactions = false;
let return_value = closure(self, previous);
self.reject_future_transactions = previous;
return_value
}
/// Imports transaction to the pool.
///
/// The pool consists of two parts: Future and Ready.
@@ -206,6 +278,10 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
// If all tags are not satisfied import to future.
if !tx.is_ready() {
if self.reject_future_transactions {
return Err(error::Error::RejectedFutureTransaction);
}
let hash = tx.transaction.hash.clone();
self.future.import(tx);
return Ok(Imported::Future { hash });
@@ -370,6 +446,11 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
removed
}
/// Removes and returns all transactions from the future queue.
pub fn clear_future(&mut self) -> Vec<Arc<Transaction<Hash, Ex>>> {
self.future.clear()
}
/// Prunes transactions that provide given list of tags.
///
/// This will cause all transactions that provide these tags to be removed from the pool,
@@ -385,7 +466,7 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
for tag in tags {
// make sure to promote any future transactions that could be unlocked
to_import.append(&mut self.future.satisfy_tags(::std::iter::once(&tag)));
to_import.append(&mut self.future.satisfy_tags(std::iter::once(&tag)));
// and actually prune transactions in ready queue
pruned.append(&mut self.ready.prune_tags(tag.clone()));
// store the tags for next submission
@@ -413,8 +494,8 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
}
/// Get pool status.
pub fn status(&self) -> Status {
Status {
pub fn status(&self) -> PoolStatus {
PoolStatus {
ready: self.ready.len(),
ready_bytes: self.ready.bytes(),
future: self.future.len(),
@@ -423,26 +504,6 @@ impl<Hash: hash::Hash + Member + Serialize, Ex: ::std::fmt::Debug> BasePool<Hash
}
}
/// Pool status
#[derive(Debug)]
pub struct Status {
/// Number of transactions in the ready queue.
pub ready: usize,
/// Sum of bytes of ready transaction encodings.
pub ready_bytes: usize,
/// Number of transactions in the future queue.
pub future: usize,
/// Sum of bytes of ready transaction encodings.
pub future_bytes: usize,
}
impl Status {
/// Returns true if the are no transactions in the pool.
pub fn is_empty(&self) -> bool {
self.ready == 0 && self.future == 0
}
}
/// Queue limits
#[derive(Debug, Clone)]
pub struct Limit {
@@ -972,4 +1033,85 @@ requires: [03,02], provides: [04], data: [4]}".to_owned()
propagate: false,
}.is_propagateable(), false);
}
#[test]
fn should_reject_future_transactions() {
// given
let mut pool = pool();
// when
pool.reject_future_transactions = true;
// then
let err = pool.import(Transaction {
data: vec![5u8],
bytes: 1,
hash: 5,
priority: 5u64,
valid_till: 64u64,
requires: vec![vec![0]],
provides: vec![],
propagate: true,
});
if let Err(error::Error::RejectedFutureTransaction) = err {
} else {
assert!(false, "Invalid error kind: {:?}", err);
}
}
#[test]
fn should_clear_future_queue() {
// given
let mut pool = pool();
// when
pool.import(Transaction {
data: vec![5u8],
bytes: 1,
hash: 5,
priority: 5u64,
valid_till: 64u64,
requires: vec![vec![0]],
provides: vec![],
propagate: true,
}).unwrap();
// then
assert_eq!(pool.future.len(), 1);
// and then when
assert_eq!(pool.clear_future().len(), 1);
// then
assert_eq!(pool.future.len(), 0);
}
#[test]
fn should_accept_future_transactions_when_explcitly_asked_to() {
// given
let mut pool = pool();
pool.reject_future_transactions = true;
// when
let flag_value = pool.with_futures_enabled(|pool, flag| {
pool.import(Transaction {
data: vec![5u8],
bytes: 1,
hash: 5,
priority: 5u64,
valid_till: 64u64,
requires: vec![vec![0]],
provides: vec![],
propagate: true,
}).unwrap();
flag
});
// then
assert_eq!(flag_value, true);
assert_eq!(pool.reject_future_transactions, true);
assert_eq!(pool.future.len(), 1);
}
}