Adds fork-awareness and finalization notifications to transaction pool watchers. (#4740)

* adds finalization support to sc-transaction-pool using MaintainedTransactionPool for finalization events

* adds TransactionStatus::Retracted, notify watchers of retracted blocks, finalized now finalizes, transactions for current finalized -> last finalized block

* adds last_finalized to ChainApi, use generic BlockT for ChainEvent

* fix tests

* Apply suggestions from code review

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

* tests

* fix tests, docs, lazily dedupe pruned hashes

* fix tests, Cargo.lock

* Apply suggestions from code review

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

* remove tree_route, last_finalized from ChainApi, add block hash to Finalization and Retracted events

* prune finality watchers

* fix tests

* remove HeaderBackend bound from FullChainApi

* code style nits, terminate stream in finality_timeout

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
Seun Lanlege
2020-02-14 14:44:58 +01:00
committed by GitHub
parent b999911bcf
commit d3a3e288b6
18 changed files with 639 additions and 283 deletions
@@ -33,7 +33,7 @@ use sp_runtime::{
traits::{self, SaturatedConversion},
transaction_validity::{TransactionValidity, TransactionTag as Tag, TransactionValidityError},
};
use sp_transaction_pool::{error, PoolStatus};
use sp_transaction_pool::error;
use wasm_timer::Instant;
use crate::validated_pool::{ValidatedPool, ValidatedTransaction};
@@ -338,34 +338,6 @@ impl<B: ChainApi> Pool<B> {
)
}
/// Return an event stream of notifications for when transactions are imported to the pool.
///
/// Consumers of this stream should use the `ready` method to actually get the
/// pending transactions in the right order.
pub fn import_notification_stream(&self) -> EventStream<ExHash<B>> {
self.validated_pool.import_notification_stream()
}
/// Invoked when extrinsics are broadcasted.
pub fn on_broadcasted(&self, propagated: HashMap<ExHash<B>, Vec<String>>) {
self.validated_pool.on_broadcasted(propagated)
}
/// Remove invalid transactions from the pool.
pub fn remove_invalid(&self, hashes: &[ExHash<B>]) -> Vec<TransactionFor<B>> {
self.validated_pool.remove_invalid(hashes)
}
/// Get an iterator for ready transactions ordered by priority
pub fn ready(&self) -> impl Iterator<Item=TransactionFor<B>> {
self.validated_pool.ready()
}
/// Returns pool status.
pub fn status(&self) -> PoolStatus {
self.validated_pool.status()
}
/// Returns transaction hash
pub fn hash_of(&self, xt: &ExtrinsicFor<B>) -> ExHash<B> {
self.validated_pool.api().hash_and_length(xt).0
@@ -454,9 +426,9 @@ impl<B: ChainApi> Pool<B> {
(hash, validity)
}
/// Get ready transaction by hash, if it present in the pool.
pub fn ready_transaction(&self, hash: &ExHash<B>) -> Option<TransactionFor<B>> {
self.validated_pool.ready_by_hash(hash)
/// get a reference to the underlying validated pool.
pub fn validated_pool(&self) -> &ValidatedPool<B> {
&self.validated_pool
}
}
@@ -598,7 +570,7 @@ mod tests {
}))).unwrap();
// then
assert_eq!(pool.ready().map(|v| v.hash).collect::<Vec<_>>(), vec![hash]);
assert_eq!(pool.validated_pool().ready().map(|v| v.hash).collect::<Vec<_>>(), vec![hash]);
}
#[test]
@@ -615,8 +587,8 @@ mod tests {
// when
pool.validated_pool.rotator().ban(&Instant::now(), vec![pool.hash_of(&uxt)]);
let res = block_on(pool.submit_one(&BlockId::Number(0), uxt));
assert_eq!(pool.status().ready, 0);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 0);
assert_eq!(pool.validated_pool().status().future, 0);
// then
assert_matches!(res.unwrap_err(), error::Error::TemporarilyBanned);
@@ -627,7 +599,7 @@ mod tests {
let stream = {
// given
let pool = pool();
let stream = pool.import_notification_stream();
let stream = pool.validated_pool().import_notification_stream();
// when
let _hash = block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer {
@@ -650,8 +622,8 @@ mod tests {
nonce: 3,
}))).unwrap();
assert_eq!(pool.status().ready, 2);
assert_eq!(pool.status().future, 1);
assert_eq!(pool.validated_pool().status().ready, 2);
assert_eq!(pool.validated_pool().status().future, 1);
stream
};
@@ -689,9 +661,9 @@ mod tests {
pool.validated_pool.clear_stale(&BlockId::Number(5)).unwrap();
// then
assert_eq!(pool.ready().count(), 0);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.status().ready, 0);
assert_eq!(pool.validated_pool().ready().count(), 0);
assert_eq!(pool.validated_pool().status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 0);
// make sure they are temporarily banned as well
assert!(pool.validated_pool.rotator().is_banned(&hash1));
assert!(pool.validated_pool.rotator().is_banned(&hash2));
@@ -735,7 +707,7 @@ mod tests {
amount: 5,
nonce: 1,
}))).unwrap();
assert_eq!(pool.status().future, 1);
assert_eq!(pool.validated_pool().status().future, 1);
// when
let hash2 = block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer {
@@ -746,7 +718,7 @@ mod tests {
}))).unwrap();
// then
assert_eq!(pool.status().future, 1);
assert_eq!(pool.validated_pool().status().future, 1);
assert!(pool.validated_pool.rotator().is_banned(&hash1));
assert!(!pool.validated_pool.rotator().is_banned(&hash2));
}
@@ -773,8 +745,8 @@ mod tests {
}))).unwrap_err();
// then
assert_eq!(pool.status().ready, 0);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 0);
assert_eq!(pool.validated_pool().status().future, 0);
}
#[test]
@@ -791,8 +763,8 @@ mod tests {
}))).unwrap_err();
// then
assert_eq!(pool.status().ready, 0);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 0);
assert_eq!(pool.validated_pool().status().future, 0);
assert_matches!(err, error::Error::NoTagsProvided);
}
@@ -809,19 +781,18 @@ mod tests {
amount: 5,
nonce: 0,
}))).unwrap();
assert_eq!(pool.status().ready, 1);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 1);
assert_eq!(pool.validated_pool().status().future, 0);
// when
block_on(pool.prune_tags(&BlockId::Number(2), vec![vec![0u8]], vec![])).unwrap();
assert_eq!(pool.status().ready, 0);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 0);
assert_eq!(pool.validated_pool().status().future, 0);
// then
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
assert_eq!(stream.next(), Some(TransactionStatus::InBlock(H256::from_low_u64_be(2).into())));
assert_eq!(stream.next(), None);
}
#[test]
@@ -834,19 +805,18 @@ mod tests {
amount: 5,
nonce: 0,
}))).unwrap();
assert_eq!(pool.status().ready, 1);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 1);
assert_eq!(pool.validated_pool().status().future, 0);
// when
block_on(pool.prune_tags(&BlockId::Number(2), vec![vec![0u8]], vec![2u64])).unwrap();
assert_eq!(pool.status().ready, 0);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 0);
assert_eq!(pool.validated_pool().status().future, 0);
// then
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
assert_eq!(stream.next(), Some(TransactionStatus::Ready));
assert_eq!(stream.next(), Some(TransactionStatus::InBlock(H256::from_low_u64_be(2).into())));
assert_eq!(stream.next(), None);
}
#[test]
@@ -859,8 +829,8 @@ mod tests {
amount: 5,
nonce: 1,
}))).unwrap();
assert_eq!(pool.status().ready, 0);
assert_eq!(pool.status().future, 1);
assert_eq!(pool.validated_pool().status().ready, 0);
assert_eq!(pool.validated_pool().status().future, 1);
// when
block_on(pool.submit_one(&BlockId::Number(0), uxt(Transfer {
@@ -869,7 +839,7 @@ mod tests {
amount: 5,
nonce: 0,
}))).unwrap();
assert_eq!(pool.status().ready, 2);
assert_eq!(pool.validated_pool().status().ready, 2);
// then
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
@@ -888,7 +858,7 @@ mod tests {
nonce: 0,
});
let watcher = block_on(pool.submit_and_watch(&BlockId::Number(0), uxt)).unwrap();
assert_eq!(pool.status().ready, 1);
assert_eq!(pool.validated_pool().status().ready, 1);
// when
pool.validated_pool.remove_invalid(&[*watcher.hash()]);
@@ -912,13 +882,13 @@ mod tests {
nonce: 0,
});
let watcher = block_on(pool.submit_and_watch(&BlockId::Number(0), uxt)).unwrap();
assert_eq!(pool.status().ready, 1);
assert_eq!(pool.validated_pool().status().ready, 1);
// when
let mut map = HashMap::new();
let peers = vec!["a".into(), "b".into(), "c".into()];
map.insert(*watcher.hash(), peers.clone());
pool.on_broadcasted(map);
pool.validated_pool().on_broadcasted(map);
// then
@@ -947,7 +917,7 @@ mod tests {
nonce: 0,
});
let watcher = block_on(pool.submit_and_watch(&BlockId::Number(0), xt)).unwrap();
assert_eq!(pool.status().ready, 1);
assert_eq!(pool.validated_pool().status().ready, 1);
// when
let xt = uxt(Transfer {
@@ -957,7 +927,7 @@ mod tests {
nonce: 1,
});
block_on(pool.submit_one(&BlockId::Number(1), xt)).unwrap();
assert_eq!(pool.status().ready, 1);
assert_eq!(pool.validated_pool().status().ready, 1);
// then
let mut stream = futures::executor::block_on_stream(watcher.into_stream());
@@ -1000,11 +970,11 @@ mod tests {
// The tag the above transaction provides (TestApi is using just nonce as u8)
let provides = vec![0_u8];
block_on(pool.submit_one(&BlockId::Number(0), xt)).unwrap();
assert_eq!(pool.status().ready, 1);
assert_eq!(pool.validated_pool().status().ready, 1);
// Now block import happens before the second transaction is able to finish verification.
block_on(pool.prune_tags(&BlockId::Number(1), vec![provides], vec![])).unwrap();
assert_eq!(pool.status().ready, 0);
assert_eq!(pool.validated_pool().status().ready, 0);
// so when we release the verification of the previous one it will have
@@ -1014,8 +984,8 @@ mod tests {
// then
is_ready.recv().unwrap(); // wait for finish
assert_eq!(pool.status().ready, 1);
assert_eq!(pool.status().future, 0);
assert_eq!(pool.validated_pool().status().ready, 1);
assert_eq!(pool.validated_pool().status().future, 0);
}
}
@@ -1047,7 +1017,7 @@ mod tests {
let tx4 = transfer(4);
let hash4 = pool.validated_pool.api().hash_and_length(&tx4).0;
let watcher4 = block_on(pool.submit_and_watch(&BlockId::Number(0), tx4)).unwrap();
assert_eq!(pool.status().ready, 5);
assert_eq!(pool.validated_pool().status().ready, 5);
// when
pool.validated_pool.api().invalidate.lock().insert(hash3);
@@ -1064,7 +1034,7 @@ mod tests {
//
// events for hash3 are: Ready, Invalid
// events for hash4 are: Ready, Invalid
assert_eq!(pool.status().ready, 2);
assert_eq!(pool.validated_pool().status().ready, 2);
assert_eq!(
futures::executor::block_on_stream(watcher3.into_stream()).collect::<Vec<_>>(),
vec![TransactionStatus::Ready, TransactionStatus::Invalid],
@@ -1095,4 +1065,3 @@ mod tests {
);
}
}