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
+101 -85
View File
@@ -37,9 +37,8 @@ use sp_runtime::{
traits::{Block as BlockT, NumberFor, AtLeast32Bit, Extrinsic},
};
use sp_transaction_pool::{
TransactionPool, PoolStatus, ImportNotificationStream,
TxHash, TransactionFor, TransactionStatusStreamFor, BlockHash,
MaintainedTransactionPool, PoolFuture,
TransactionPool, PoolStatus, ImportNotificationStream, TxHash, TransactionFor,
TransactionStatusStreamFor, MaintainedTransactionPool, PoolFuture, ChainEvent,
};
use wasm_timer::Instant;
@@ -115,7 +114,6 @@ impl<PoolApi, Block> BasicPool<PoolApi, Block>
}
)),
}
}
/// Gets shared reference to the underlying pool.
@@ -174,19 +172,19 @@ impl<PoolApi, Block> TransactionPool for BasicPool<PoolApi, Block>
}
fn remove_invalid(&self, hashes: &[TxHash<Self>]) -> Vec<Arc<Self::InPoolTransaction>> {
self.pool.remove_invalid(hashes)
self.pool.validated_pool().remove_invalid(hashes)
}
fn status(&self) -> PoolStatus {
self.pool.status()
self.pool.validated_pool().status()
}
fn ready(&self) -> Box<dyn Iterator<Item=Arc<Self::InPoolTransaction>>> {
Box::new(self.pool.ready())
Box::new(self.pool.validated_pool().ready())
}
fn import_notification_stream(&self) -> ImportNotificationStream<TxHash<Self>> {
self.pool.import_notification_stream()
self.pool.validated_pool().import_notification_stream()
}
fn hash_of(&self, xt: &TransactionFor<Self>) -> TxHash<Self> {
@@ -194,11 +192,11 @@ impl<PoolApi, Block> TransactionPool for BasicPool<PoolApi, Block>
}
fn on_broadcasted(&self, propagations: HashMap<TxHash<Self>, Vec<String>>) {
self.pool.on_broadcasted(propagations)
self.pool.validated_pool().on_broadcasted(propagations)
}
fn ready_transaction(&self, hash: &TxHash<Self>) -> Option<Arc<Self::InPoolTransaction>> {
self.pool.ready_transaction(hash)
self.pool.validated_pool().ready_by_hash(hash)
}
}
@@ -214,7 +212,7 @@ enum RevalidationStatus<N> {
enum RevalidationStrategy<N> {
Always,
Light(RevalidationStatus<N>)
Light(RevalidationStatus<N>),
}
struct RevalidationAction {
@@ -241,7 +239,7 @@ impl<N: Clone + Copy + AtLeast32Bit> RevalidationStrategy<N> {
revalidate: status.next_required(
block,
revalidate_time_period,
revalidate_block_period
revalidate_block_period,
),
resubmit: false,
revalidate_amount: None,
@@ -275,7 +273,7 @@ impl<N: Clone + Copy + AtLeast32Bit> RevalidationStatus<N> {
revalidate_block_period.map(|period| block + period),
);
false
},
}
Self::Scheduled(revalidate_at_time, revalidate_at_block) => {
let is_required = revalidate_at_time.map(|at| Instant::now() >= at).unwrap_or(false)
|| revalidate_at_block.map(|at| block >= at).unwrap_or(false);
@@ -283,87 +281,105 @@ impl<N: Clone + Copy + AtLeast32Bit> RevalidationStatus<N> {
*self = Self::InProgress;
}
is_required
},
}
Self::InProgress => false,
}
}
}
impl<PoolApi, Block> MaintainedTransactionPool for BasicPool<PoolApi, Block>
where
Block: BlockT,
PoolApi: 'static + sc_transaction_graph::ChainApi<Block=Block, Hash=Block::Hash>,
where
Block: BlockT,
PoolApi: 'static + sc_transaction_graph::ChainApi<Block=Block, Hash=Block::Hash>,
{
fn maintain(&self, id: &BlockId<Self::Block>, retracted: &[BlockHash<Self>])
-> Pin<Box<dyn Future<Output=()> + Send>>
{
let id = id.clone();
let pool = self.pool.clone();
let api = self.api.clone();
fn maintain(&self, event: ChainEvent<Self::Block>) -> Pin<Box<dyn Future<Output=()> + Send>> {
match event {
ChainEvent::NewBlock { id, retracted, .. } => {
let id = id.clone();
let pool = self.pool.clone();
let api = self.api.clone();
let block_number = match api.block_id_to_number(&id) {
Ok(Some(number)) => number,
_ => {
log::trace!(target: "txqueue", "Skipping chain event - no number for that block {:?}", id);
return Box::pin(ready(()));
let block_number = match api.block_id_to_number(&id) {
Ok(Some(number)) => number,
_ => {
log::trace!(target: "txqueue", "Skipping chain event - no number for that block {:?}", id);
return Box::pin(ready(()));
}
};
let next_action = self.revalidation_strategy.lock().next(
block_number,
Some(std::time::Duration::from_secs(60)),
Some(20.into()),
);
let revalidation_strategy = self.revalidation_strategy.clone();
let retracted = retracted.clone();
async move {
// We don't query block if we won't prune anything
if !pool.validated_pool().status().is_empty() {
let hashes = api.block_body(&id).await
.unwrap_or_else(|e| {
log::warn!("Prune known transactions: error request {:?}!", e);
None
})
.unwrap_or_default()
.into_iter()
.map(|tx| pool.hash_of(&tx))
.collect::<Vec<_>>();
if let Err(e) = pool.prune_known(&id, &hashes) {
log::error!("Cannot prune known in the pool {:?}!", e);
}
}
if next_action.resubmit {
let mut resubmit_transactions = Vec::new();
for retracted_hash in retracted {
// notify txs awaiting finality that it has been retracted
pool.validated_pool().on_block_retracted(retracted_hash.clone());
let block_transactions = api.block_body(&BlockId::hash(retracted_hash.clone())).await
.unwrap_or_else(|e| {
log::warn!("Failed to fetch block body {:?}!", e);
None
})
.unwrap_or_default()
.into_iter()
.filter(|tx| tx.is_signed().unwrap_or(true));
resubmit_transactions.extend(block_transactions);
}
if let Err(e) = pool.submit_at(&id, resubmit_transactions, true).await {
log::debug!(
target: "txpool",
"[{:?}] Error re-submitting transactions: {:?}", id, e
)
}
}
if next_action.revalidate {
if let Err(e) = pool.revalidate_ready(&id, next_action.revalidate_amount).await {
log::warn!("Revalidate ready failed {:?}", e);
}
}
revalidation_strategy.lock().clear();
}.boxed()
}
};
let next_action = self.revalidation_strategy.lock().next(
block_number,
Some(std::time::Duration::from_secs(60)),
Some(20.into()),
);
let revalidation_strategy = self.revalidation_strategy.clone();
let retracted = retracted.to_vec();
async move {
// We don't query block if we won't prune anything
if !pool.status().is_empty() {
let hashes = api.block_body(&id).await
.unwrap_or_else(|e| {
log::warn!("Prune known transactions: error request {:?}!", e);
None
})
.unwrap_or_default()
.into_iter()
.map(|tx| pool.hash_of(&tx))
.collect::<Vec<_>>();
if let Err(e) = pool.prune_known(&id, &hashes) {
log::error!("Cannot prune known in the pool {:?}!", e);
}
ChainEvent::Finalized { hash } => {
let pool = self.pool.clone();
async move {
if let Err(e) = pool.validated_pool().on_block_finalized(hash).await {
log::warn!(
target: "txpool",
"Error [{}] occurred while attempting to notify watchers of finalization {}",
e, hash
)
}
}.boxed()
}
if next_action.resubmit {
let mut resubmit_transactions = Vec::new();
for retracted_hash in retracted {
let block_transactions = api.block_body(&BlockId::hash(retracted_hash.clone())).await
.unwrap_or_else(|e| {
log::warn!("Failed to fetch block body {:?}!", e);
None
})
.unwrap_or_default()
.into_iter()
.filter(|tx| tx.is_signed().unwrap_or(true));
resubmit_transactions.extend(block_transactions);
}
if let Err(e) = pool.submit_at(&id, resubmit_transactions, true).await {
log::debug!(target: "txpool",
"[{:?}] Error re-submitting transactions: {:?}", id, e
)
}
}
if next_action.revalidate {
if let Err(e) = pool.revalidate_ready(&id, next_action.revalidate_amount).await {
log::warn!("Revalidate ready failed {:?}", e);
}
}
revalidation_strategy.lock().clear();
}.boxed()
}
}
}