use crate::{
common::api::FullChainApi,
fork_aware_txpool::ForkAwareTxPool as ForkAwareFullPool,
graph::{base_pool::Transaction, ChainApi, ExtrinsicFor, ExtrinsicHash, IsValidator, Options},
single_state_txpool::BasicPool as SingleStateFullPool,
TransactionPoolWrapper, LOG_TARGET,
};
use prometheus_endpoint::Registry as PrometheusRegistry;
use sc_transaction_pool_api::{LocalTransactionPool, MaintainedTransactionPool};
use sp_core::traits::SpawnEssentialNamed;
use sp_runtime::traits::Block as BlockT;
use std::{marker::PhantomData, sync::Arc, time::Duration};
#[derive(Debug, Clone)]
pub enum TransactionPoolType {
SingleState,
ForkAware,
}
#[derive(Debug, Clone)]
pub struct TransactionPoolOptions {
txpool_type: TransactionPoolType,
options: Options,
}
impl Default for TransactionPoolOptions {
fn default() -> Self {
Self { txpool_type: TransactionPoolType::SingleState, options: Default::default() }
}
}
impl TransactionPoolOptions {
pub fn new_with_params(
pool_limit: usize,
pool_bytes: usize,
tx_ban_seconds: Option<u64>,
txpool_type: TransactionPoolType,
is_dev: bool,
) -> TransactionPoolOptions {
let mut options = Options::default();
options.ready.count = pool_limit;
options.ready.total_bytes = pool_bytes;
let factor = 10;
options.future.count = pool_limit / factor;
options.future.total_bytes = pool_bytes / factor;
options.ban_time = if let Some(ban_seconds) = tx_ban_seconds {
Duration::from_secs(ban_seconds)
} else if is_dev {
Duration::from_secs(0)
} else {
Duration::from_secs(30 * 60)
};
TransactionPoolOptions { options, txpool_type }
}
pub fn new_for_benchmarks() -> TransactionPoolOptions {
TransactionPoolOptions {
options: Options {
ready: crate::graph::base_pool::Limit {
count: 100_000,
total_bytes: 100 * 1024 * 1024,
},
future: crate::graph::base_pool::Limit {
count: 100_000,
total_bytes: 100 * 1024 * 1024,
},
reject_future_transactions: false,
ban_time: Duration::from_secs(30 * 60),
},
txpool_type: TransactionPoolType::SingleState,
}
}
}
pub trait FullClientTransactionPool<Block, Client>:
MaintainedTransactionPool<
Block = Block,
Hash = ExtrinsicHash<FullChainApi<Client, Block>>,
InPoolTransaction = Transaction<
ExtrinsicHash<FullChainApi<Client, Block>>,
ExtrinsicFor<FullChainApi<Client, Block>>,
>,
Error = <FullChainApi<Client, Block> as ChainApi>::Error,
> + LocalTransactionPool<
Block = Block,
Hash = ExtrinsicHash<FullChainApi<Client, Block>>,
Error = <FullChainApi<Client, Block> as ChainApi>::Error,
>
where
Block: BlockT,
Client: sp_api::ProvideRuntimeApi<Block>
+ sc_client_api::BlockBackend<Block>
+ sc_client_api::blockchain::HeaderBackend<Block>
+ sp_runtime::traits::BlockIdTo<Block>
+ sp_blockchain::HeaderMetadata<Block, Error = sp_blockchain::Error>
+ 'static,
Client::Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>,
{
}
impl<Block, Client, P> FullClientTransactionPool<Block, Client> for P
where
Block: BlockT,
Client: sp_api::ProvideRuntimeApi<Block>
+ sc_client_api::BlockBackend<Block>
+ sc_client_api::blockchain::HeaderBackend<Block>
+ sp_runtime::traits::BlockIdTo<Block>
+ sp_blockchain::HeaderMetadata<Block, Error = sp_blockchain::Error>
+ 'static,
Client::Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>,
P: MaintainedTransactionPool<
Block = Block,
Hash = ExtrinsicHash<FullChainApi<Client, Block>>,
InPoolTransaction = Transaction<
ExtrinsicHash<FullChainApi<Client, Block>>,
ExtrinsicFor<FullChainApi<Client, Block>>,
>,
Error = <FullChainApi<Client, Block> as ChainApi>::Error,
> + LocalTransactionPool<
Block = Block,
Hash = ExtrinsicHash<FullChainApi<Client, Block>>,
Error = <FullChainApi<Client, Block> as ChainApi>::Error,
>,
{
}
pub type TransactionPoolHandle<Block, Client> = TransactionPoolWrapper<Block, Client>;
pub struct Builder<'a, Block, Client> {
options: TransactionPoolOptions,
is_validator: IsValidator,
prometheus: Option<&'a PrometheusRegistry>,
client: Arc<Client>,
spawner: Box<dyn SpawnEssentialNamed>,
_phantom: PhantomData<(Client, Block)>,
}
impl<'a, Client, Block> Builder<'a, Block, Client>
where
Block: BlockT,
Client: sp_api::ProvideRuntimeApi<Block>
+ sc_client_api::BlockBackend<Block>
+ sc_client_api::blockchain::HeaderBackend<Block>
+ sp_runtime::traits::BlockIdTo<Block>
+ sc_client_api::ExecutorProvider<Block>
+ sc_client_api::UsageProvider<Block>
+ sp_blockchain::HeaderMetadata<Block, Error = sp_blockchain::Error>
+ Send
+ Sync
+ 'static,
<Block as BlockT>::Hash: std::marker::Unpin,
Client::Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>,
{
pub fn new(
spawner: impl SpawnEssentialNamed + 'static,
client: Arc<Client>,
is_validator: IsValidator,
) -> Builder<'a, Block, Client> {
Builder {
options: Default::default(),
_phantom: Default::default(),
spawner: Box::new(spawner),
client,
is_validator,
prometheus: None,
}
}
pub fn with_options(mut self, options: TransactionPoolOptions) -> Self {
self.options = options;
self
}
pub fn with_prometheus(mut self, prometheus: Option<&'a PrometheusRegistry>) -> Self {
self.prometheus = prometheus;
self
}
pub fn build(self) -> TransactionPoolHandle<Block, Client> {
log::info!(target:LOG_TARGET, " creating {:?} txpool {:?}/{:?}.", self.options.txpool_type, self.options.options.ready, self.options.options.future);
TransactionPoolWrapper::<Block, Client>(match self.options.txpool_type {
TransactionPoolType::SingleState => Box::new(SingleStateFullPool::new_full(
self.options.options,
self.is_validator,
self.prometheus,
self.spawner,
self.client,
)),
TransactionPoolType::ForkAware => Box::new(ForkAwareFullPool::new_full(
self.options.options,
self.is_validator,
self.prometheus,
self.spawner,
self.client,
)),
})
}
}