// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see .
use std::{
marker::{PhantomData, Unpin},
sync::Arc,
time::Instant,
};
use futures::{
Future, FutureExt,
future::{Either, join, ready},
};
use log::warn;
use parking_lot::Mutex;
use client_api::{
client::BlockBody,
light::{Fetcher, RemoteBodyRequest},
};
use primitives::{Blake2Hasher, H256};
use sr_primitives::{
generic::BlockId,
traits::{Block as BlockT, Extrinsic, Header, NumberFor, ProvideRuntimeApi, SimpleArithmetic},
};
use sp_blockchain::HeaderBackend;
use txpool_api::TransactionPoolMaintainer;
use txpool_runtime_api::TaggedTransactionQueue;
use txpool::{self, ChainApi};
/// Basic transaction pool maintainer for full clients.
pub struct FullBasicPoolMaintainer {
pool: Arc>,
client: Arc,
}
impl FullBasicPoolMaintainer {
/// Create new basic full pool maintainer.
pub fn new(
pool: Arc>,
client: Arc,
) -> Self {
FullBasicPoolMaintainer { pool, client }
}
}
impl TransactionPoolMaintainer
for
FullBasicPoolMaintainer
where
Block: BlockT::Out>,
Client: ProvideRuntimeApi + HeaderBackend + BlockBody + 'static,
Client::Api: TaggedTransactionQueue,
PoolApi: ChainApi + 'static,
{
type Block = Block;
type Hash = Block::Hash;
fn maintain(
&self,
id: &BlockId,
retracted: &[Block::Hash],
) -> Box + Send + Unpin> {
// Put transactions from retracted blocks back into the pool.
let client_copy = self.client.clone();
let retracted_transactions = retracted.to_vec().into_iter()
.filter_map(move |hash| client_copy.block_body(&BlockId::hash(hash)).ok().unwrap_or(None))
.flat_map(|block| block.into_iter())
.filter(|tx| tx.is_signed().unwrap_or(false));
let resubmit_future = self.pool
.submit_at(id, retracted_transactions, true)
.then(|resubmit_result| ready(match resubmit_result {
Ok(_) => (),
Err(e) => {
warn!("Error re-submitting transactions: {:?}", e);
()
}
}));
// Avoid calling into runtime if there is nothing to prune from the pool anyway.
if self.pool.status().is_empty() {
return Box::new(resubmit_future)
}
let block = (self.client.header(*id), self.client.block_body(id));
match block {
(Ok(Some(header)), Ok(Some(extrinsics))) => {
let parent_id = BlockId::hash(*header.parent_hash());
let prune_future = self.pool
.prune(id, &parent_id, &extrinsics)
.then(|prune_result| ready(match prune_result {
Ok(_) => (),
Err(e) => {
warn!("Error pruning transactions: {:?}", e);
()
}
}));
Box::new(resubmit_future.then(|_| prune_future))
},
(Ok(_), Ok(_)) => Box::new(resubmit_future),
err => {
warn!("Error reading block: {:?}", err);
Box::new(resubmit_future)
},
}
}
}
/// Basic transaction pool maintainer for light clients.
pub struct LightBasicPoolMaintainer {
pool: Arc>,
client: Arc,
fetcher: Arc,
revalidate_time_period: Option,
revalidate_block_period: Option>,
revalidation_status: Arc>>>,
_phantom: PhantomData,
}
impl LightBasicPoolMaintainer
where
Block: BlockT::Out>,
Client: ProvideRuntimeApi + HeaderBackend + BlockBody + 'static,
Client::Api: TaggedTransactionQueue,
PoolApi: ChainApi + 'static,
F: Fetcher + 'static,
{
/// Create light pool maintainer with default constants.
///
/// Default constants are: revalidate every 60 seconds or every 20 blocks
/// (whatever happens first).
pub fn with_defaults(
pool: Arc>,
client: Arc,
fetcher: Arc,
) -> Self {
Self::new(
pool,
client,
fetcher,
Some(std::time::Duration::from_secs(60)),
Some(20.into()),
)
}
/// Create light pool maintainer with passed constants.
pub fn new(
pool: Arc>,
client: Arc,
fetcher: Arc,
revalidate_time_period: Option,
revalidate_block_period: Option>,
) -> Self {
Self {
pool,
client,
fetcher,
revalidate_time_period,
revalidate_block_period,
revalidation_status: Arc::new(Mutex::new(TxPoolRevalidationStatus::NotScheduled)),
_phantom: Default::default(),
}
}
/// Returns future that prunes block transactions from the pool.
fn prune(
&self,
id: &BlockId,
header: &Block::Header,
) -> impl std::future::Future