// Copyright 2019-2020 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, debug, trace};
use parking_lot::Mutex;
use sc_client_api::{
client::BlockBody,
light::{Fetcher, RemoteBodyRequest},
};
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Extrinsic, Header, NumberFor, SimpleArithmetic},
};
use sp_blockchain::HeaderBackend;
use sp_transaction_pool::{TransactionPoolMaintainer, runtime_api::TaggedTransactionQueue};
use sp_api::ProvideRuntimeApi;
use sc_transaction_graph::{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,
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> {
let now = std::time::Instant::now();
let took = move || format!("Took {} ms", now.elapsed().as_millis());
let id = *id;
trace!(target: "txpool", "[{:?}] Starting pool maintainance", id);
// 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())
// if signed information is not present, attempt to resubmit anyway.
.filter(|tx| tx.is_signed().unwrap_or(true));
let resubmit_future = self.pool
.submit_at(&id, retracted_transactions, true)
.then(move |resubmit_result| ready(match resubmit_result {
Ok(_) => trace!(target: "txpool",
"[{:?}] Re-submitting retracted done. {}", id, took()
),
Err(e) => debug!(target: "txpool",
"[{:?}] Error re-submitting transactions: {:?}", id, 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));
let prune_future = 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(move |prune_result| ready(match prune_result {
Ok(_) => trace!(target: "txpool",
"[{:?}] Pruning done. {}", id, took()
),
Err(e) => warn!(target: "txpool",
"[{:?}] Error pruning transactions: {:?}", id, e
),
}));
Either::Left(resubmit_future.then(|_| prune_future))
},
(Ok(_), Ok(_)) => Either::Right(resubmit_future),
err => {
warn!(target: "txpool", "[{:?}] Error reading block: {:?}", id, err);
Either::Right(resubmit_future)
},
};
let revalidate_future = self.pool
.revalidate_ready(&id, Some(16))
.then(move |result| ready(match result {
Ok(_) => debug!(target: "txpool",
"[{:?}] Revalidation done: {}", id, took()
),
Err(e) => warn!(target: "txpool",
"[{:?}] Encountered errors while revalidating transactions: {:?}", id, e
),
}));
Box::new(prune_future.then(|_| revalidate_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,
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