Skip to main content

soil_txpool/
builder.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Utility for building substrate transaction pool trait object.
8
9use 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/// The type of transaction pool.
23#[derive(Debug, Clone)]
24pub enum TransactionPoolType {
25	/// Single-state transaction pool
26	SingleState,
27	/// Fork-aware transaction pool
28	ForkAware,
29}
30
31/// Transaction pool options.
32#[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	/// Creates the options for the transaction pool using given parameters.
46	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		// ready queue
56		options.ready.count = pool_limit;
57		options.ready.total_bytes = pool_bytes;
58
59		// future queue
60		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	/// Creates predefined options for benchmarking
76	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
95/// `FullClientTransactionPool` is a trait that combines the functionality of
96/// `MaintainedTransactionPool` and `LocalTransactionPool` for a given `Client` and `Block`.
97///
98/// This trait defines the requirements for a full client transaction pool, ensuring
99/// that it can handle transactions submission and maintenance.
100pub 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
152/// The public type alias for the actual type providing the implementation of
153/// `FullClientTransactionPool` with the given `Client` and `Block` types.
154///
155/// This handle abstracts away the specific type of the transaction pool. Should be used
156/// externally to keep reference to transaction pool.
157pub type TransactionPoolHandle<Block, Client> = TransactionPoolWrapper<Block, Client>;
158
159/// Builder allowing to create specific instance of transaction pool.
160pub 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	/// Creates new instance of `Builder`
186	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	/// Sets the options used for creating a transaction pool instance.
202	pub fn with_options(mut self, options: TransactionPoolOptions) -> Self {
203		self.options = options;
204		self
205	}
206
207	/// Sets the prometheus endpoint used in a transaction pool instance.
208	pub fn with_prometheus(mut self, prometheus: Option<&'a PrometheusRegistry>) -> Self {
209		self.prometheus = prometheus;
210		self
211	}
212
213	/// Creates an instance of transaction pool.
214	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}