1use crate::{
10 common::api::FullChainApi,
11 fork_aware_txpool::ForkAwareTxPool as ForkAwareFullPool,
12 graph::{base_pool::Transaction, ChainApi, ExtrinsicFor, ExtrinsicHash, IsValidator, Options},
13 single_state_txpool::BasicPool as SingleStateFullPool,
14 TransactionPoolWrapper, LOG_TARGET,
15};
16use soil_prometheus::Registry as PrometheusRegistry;
17use soil_client::transaction_pool::{LocalTransactionPool, MaintainedTransactionPool};
18use std::{marker::PhantomData, sync::Arc, time::Duration};
19use subsoil::core::traits::SpawnEssentialNamed;
20use subsoil::runtime::traits::Block as BlockT;
21
22#[derive(Debug, Clone)]
24pub enum TransactionPoolType {
25 SingleState,
27 ForkAware,
29}
30
31#[derive(Debug, Clone)]
33pub struct TransactionPoolOptions {
34 txpool_type: TransactionPoolType,
35 options: Options,
36}
37
38impl Default for TransactionPoolOptions {
39 fn default() -> Self {
40 Self { txpool_type: TransactionPoolType::SingleState, options: Default::default() }
41 }
42}
43
44impl TransactionPoolOptions {
45 pub fn new_with_params(
47 pool_limit: usize,
48 pool_bytes: usize,
49 tx_ban_seconds: Option<u64>,
50 txpool_type: TransactionPoolType,
51 is_dev: bool,
52 ) -> TransactionPoolOptions {
53 let mut options = Options::default();
54
55 options.ready.count = pool_limit;
57 options.ready.total_bytes = pool_bytes;
58
59 let factor = 10;
61 options.future.count = pool_limit / factor;
62 options.future.total_bytes = pool_bytes / factor;
63
64 options.ban_time = if let Some(ban_seconds) = tx_ban_seconds {
65 Duration::from_secs(ban_seconds)
66 } else if is_dev {
67 Duration::from_secs(0)
68 } else {
69 Duration::from_secs(30 * 60)
70 };
71
72 TransactionPoolOptions { options, txpool_type }
73 }
74
75 pub fn new_for_benchmarks() -> TransactionPoolOptions {
77 TransactionPoolOptions {
78 options: Options {
79 ready: crate::graph::base_pool::Limit {
80 count: 100_000,
81 total_bytes: 100 * 1024 * 1024,
82 },
83 future: crate::graph::base_pool::Limit {
84 count: 100_000,
85 total_bytes: 100 * 1024 * 1024,
86 },
87 reject_future_transactions: false,
88 ban_time: Duration::from_secs(30 * 60),
89 },
90 txpool_type: TransactionPoolType::SingleState,
91 }
92 }
93}
94
95pub trait FullClientTransactionPool<Block, Client>:
101 MaintainedTransactionPool<
102 Block = Block,
103 Hash = ExtrinsicHash<FullChainApi<Client, Block>>,
104 InPoolTransaction = Transaction<
105 ExtrinsicHash<FullChainApi<Client, Block>>,
106 ExtrinsicFor<FullChainApi<Client, Block>>,
107 >,
108 Error = <FullChainApi<Client, Block> as ChainApi>::Error,
109 > + LocalTransactionPool<
110 Block = Block,
111 Hash = ExtrinsicHash<FullChainApi<Client, Block>>,
112 Error = <FullChainApi<Client, Block> as ChainApi>::Error,
113 >
114where
115 Block: BlockT,
116 Client: subsoil::api::ProvideRuntimeApi<Block>
117 + soil_client::client_api::BlockBackend<Block>
118 + soil_client::client_api::blockchain::HeaderBackend<Block>
119 + subsoil::runtime::traits::BlockIdTo<Block>
120 + soil_client::blockchain::HeaderMetadata<Block, Error = soil_client::blockchain::Error>
121 + 'static,
122 Client::Api: subsoil::txpool::runtime_api::TaggedTransactionQueue<Block>,
123{
124}
125
126impl<Block, Client, P> FullClientTransactionPool<Block, Client> for P
127where
128 Block: BlockT,
129 Client: subsoil::api::ProvideRuntimeApi<Block>
130 + soil_client::client_api::BlockBackend<Block>
131 + soil_client::client_api::blockchain::HeaderBackend<Block>
132 + subsoil::runtime::traits::BlockIdTo<Block>
133 + soil_client::blockchain::HeaderMetadata<Block, Error = soil_client::blockchain::Error>
134 + 'static,
135 Client::Api: subsoil::txpool::runtime_api::TaggedTransactionQueue<Block>,
136 P: MaintainedTransactionPool<
137 Block = Block,
138 Hash = ExtrinsicHash<FullChainApi<Client, Block>>,
139 InPoolTransaction = Transaction<
140 ExtrinsicHash<FullChainApi<Client, Block>>,
141 ExtrinsicFor<FullChainApi<Client, Block>>,
142 >,
143 Error = <FullChainApi<Client, Block> as ChainApi>::Error,
144 > + LocalTransactionPool<
145 Block = Block,
146 Hash = ExtrinsicHash<FullChainApi<Client, Block>>,
147 Error = <FullChainApi<Client, Block> as ChainApi>::Error,
148 >,
149{
150}
151
152pub type TransactionPoolHandle<Block, Client> = TransactionPoolWrapper<Block, Client>;
158
159pub struct Builder<'a, Block, Client> {
161 options: TransactionPoolOptions,
162 is_validator: IsValidator,
163 prometheus: Option<&'a PrometheusRegistry>,
164 client: Arc<Client>,
165 spawner: Box<dyn SpawnEssentialNamed>,
166 _phantom: PhantomData<(Client, Block)>,
167}
168
169impl<'a, Client, Block> Builder<'a, Block, Client>
170where
171 Block: BlockT,
172 Client: subsoil::api::ProvideRuntimeApi<Block>
173 + soil_client::client_api::BlockBackend<Block>
174 + soil_client::client_api::blockchain::HeaderBackend<Block>
175 + subsoil::runtime::traits::BlockIdTo<Block>
176 + soil_client::client_api::ExecutorProvider<Block>
177 + soil_client::client_api::UsageProvider<Block>
178 + soil_client::blockchain::HeaderMetadata<Block, Error = soil_client::blockchain::Error>
179 + Send
180 + Sync
181 + 'static,
182 <Block as BlockT>::Hash: std::marker::Unpin,
183 Client::Api: subsoil::txpool::runtime_api::TaggedTransactionQueue<Block>,
184{
185 pub fn new(
187 spawner: impl SpawnEssentialNamed + 'static,
188 client: Arc<Client>,
189 is_validator: IsValidator,
190 ) -> Builder<'a, Block, Client> {
191 Builder {
192 options: Default::default(),
193 _phantom: Default::default(),
194 spawner: Box::new(spawner),
195 client,
196 is_validator,
197 prometheus: None,
198 }
199 }
200
201 pub fn with_options(mut self, options: TransactionPoolOptions) -> Self {
203 self.options = options;
204 self
205 }
206
207 pub fn with_prometheus(mut self, prometheus: Option<&'a PrometheusRegistry>) -> Self {
209 self.prometheus = prometheus;
210 self
211 }
212
213 pub fn build(self) -> TransactionPoolHandle<Block, Client> {
215 tracing::info!(
216 target: LOG_TARGET,
217 txpool_type = ?self.options.txpool_type,
218 ready = ?self.options.options.ready,
219 future = ?self.options.options.future,
220 "Creating transaction pool"
221 );
222 TransactionPoolWrapper::<Block, Client>(match self.options.txpool_type {
223 TransactionPoolType::SingleState => Box::new(SingleStateFullPool::new_full(
224 self.options.options,
225 self.is_validator,
226 self.prometheus,
227 self.spawner,
228 self.client,
229 )),
230 TransactionPoolType::ForkAware => Box::new(ForkAwareFullPool::new_full(
231 self.options.options,
232 self.is_validator,
233 self.prometheus,
234 self.spawner,
235 self.client,
236 )),
237 })
238 }
239}