referrerpolicy=no-referrer-when-downgrade

sc_transaction_pool/
builder.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Utility for building substrate transaction pool trait object.
20
21use crate::{
22	fork_aware_txpool::ForkAwareTxPool as ForkAwareFullPool,
23	graph::{base_pool::Transaction, IsValidator, Options},
24	single_state_txpool::BasicPool as SingleStateFullPool,
25	LOG_TARGET,
26};
27use prometheus_endpoint::Registry as PrometheusRegistry;
28use sc_transaction_pool_api::{LocalTransactionPool, MaintainedTransactionPool};
29use sp_core::traits::SpawnEssentialNamed;
30use sp_runtime::traits::Block as BlockT;
31use std::{marker::PhantomData, sync::Arc, time::Duration};
32
33/// The type of transaction pool.
34#[derive(Debug, Clone)]
35pub enum TransactionPoolType {
36	/// Single-state transaction pool
37	SingleState,
38	/// Fork-aware transaction pool
39	ForkAware,
40}
41
42/// Transaction pool options.
43#[derive(Debug, Clone)]
44pub struct TransactionPoolOptions {
45	txpool_type: TransactionPoolType,
46	options: Options,
47	/// If `true`, the pool is only maintained on best blocks (legacy behavior).
48	///
49	/// Only relevant for the fork-aware pool.
50	best_blocks_only: bool,
51}
52
53impl Default for TransactionPoolOptions {
54	fn default() -> Self {
55		Self {
56			txpool_type: TransactionPoolType::SingleState,
57			options: Default::default(),
58			best_blocks_only: false,
59		}
60	}
61}
62
63impl TransactionPoolOptions {
64	/// Creates the options for the transaction pool using given parameters.
65	pub fn new_with_params(
66		pool_limit: usize,
67		pool_bytes: usize,
68		tx_ban_seconds: Option<u64>,
69		txpool_type: TransactionPoolType,
70		is_dev: bool,
71		best_blocks_only: bool,
72	) -> TransactionPoolOptions {
73		let mut options = Options::default();
74
75		// ready queue
76		options.ready.count = pool_limit;
77		options.ready.total_bytes = pool_bytes;
78
79		// future queue
80		let factor = 10;
81		options.future.count = pool_limit / factor;
82		options.future.total_bytes = pool_bytes / factor;
83
84		options.ban_time = if let Some(ban_seconds) = tx_ban_seconds {
85			Duration::from_secs(ban_seconds)
86		} else if is_dev {
87			Duration::from_secs(0)
88		} else {
89			Duration::from_secs(30 * 60)
90		};
91
92		TransactionPoolOptions { options, txpool_type, best_blocks_only }
93	}
94
95	/// Creates predefined options for benchmarking
96	pub fn new_for_benchmarks() -> TransactionPoolOptions {
97		TransactionPoolOptions {
98			options: Options {
99				ready: crate::graph::base_pool::Limit {
100					count: 100_000,
101					total_bytes: 100 * 1024 * 1024,
102				},
103				future: crate::graph::base_pool::Limit {
104					count: 100_000,
105					total_bytes: 100 * 1024 * 1024,
106				},
107				reject_future_transactions: false,
108				ban_time: Duration::from_secs(30 * 60),
109			},
110			txpool_type: TransactionPoolType::SingleState,
111			best_blocks_only: false,
112		}
113	}
114
115	/// Returns whether the transaction pool should be notified about *every* imported block.
116	pub fn use_all_block_notifications(&self) -> bool {
117		matches!(self.txpool_type, TransactionPoolType::ForkAware) && !self.best_blocks_only
118	}
119}
120
121/// The client capabilities the transaction pool of a full node relies on.
122///
123/// It is blanket implemented, so every client able to validate transactions against the runtime
124/// qualifies. Only the code that actually builds a pool needs it: the [`Builder`] and the
125/// [`crate::FullChainApi`] it wires up. Holding a [`TransactionPoolHandle`] requires no client
126/// bound at all, since the handle is parameterized by the block type alone.
127pub trait ClientForTransactionPool<Block: BlockT>:
128	sp_api::ProvideRuntimeApi<
129		Block,
130		Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>,
131	> + sc_client_api::BlockBackend<Block>
132	+ sc_client_api::blockchain::HeaderBackend<Block>
133	+ sp_runtime::traits::BlockIdTo<Block>
134	+ sp_blockchain::HeaderMetadata<Block, Error = sp_blockchain::Error>
135	+ 'static
136{
137}
138
139impl<Block: BlockT, T> ClientForTransactionPool<Block> for T where
140	T: sp_api::ProvideRuntimeApi<
141			Block,
142			Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>,
143		> + sc_client_api::BlockBackend<Block>
144		+ sc_client_api::blockchain::HeaderBackend<Block>
145		+ sp_runtime::traits::BlockIdTo<Block>
146		+ sp_blockchain::HeaderMetadata<Block, Error = sp_blockchain::Error>
147		+ 'static
148{
149}
150
151/// `FullClientTransactionPool` is a trait that combines the functionality of
152/// `MaintainedTransactionPool` and `LocalTransactionPool` for a given `Block`.
153///
154/// This trait defines the requirements for a full client transaction pool, ensuring
155/// that it can handle transactions submission and maintenance.
156///
157/// The associated types are fully determined by `Block`, so the client used to build the pool does
158/// not appear here.
159pub trait FullClientTransactionPool<Block>: MaintainedTransactionPool<
160		Block = Block,
161		Hash = <Block as BlockT>::Hash,
162		InPoolTransaction = Transaction<<Block as BlockT>::Hash, Arc<<Block as BlockT>::Extrinsic>>,
163		Error = crate::error::Error,
164	> + LocalTransactionPool<Block = Block, Hash = <Block as BlockT>::Hash, Error = crate::error::Error>
165where
166	Block: BlockT,
167{
168}
169
170impl<Block, P> FullClientTransactionPool<Block> for P
171where
172	Block: BlockT,
173	P: MaintainedTransactionPool<
174			Block = Block,
175			Hash = <Block as BlockT>::Hash,
176			InPoolTransaction = Transaction<
177				<Block as BlockT>::Hash,
178				Arc<<Block as BlockT>::Extrinsic>,
179			>,
180			Error = crate::error::Error,
181		> + LocalTransactionPool<
182			Block = Block,
183			Hash = <Block as BlockT>::Hash,
184			Error = crate::error::Error,
185		>,
186{
187}
188
189/// The public type alias for the trait object providing the implementation of
190/// `FullClientTransactionPool` for the given `Block` type.
191///
192/// This handle abstracts away the specific type of the transaction pool, e.g. fork-aware or
193/// single-state. It is unsized, so it is always used behind an `Arc`.
194pub type TransactionPoolHandle<Block> = dyn FullClientTransactionPool<Block>;
195
196/// Builder allowing to create specific instance of transaction pool.
197pub struct Builder<'a, Block, Client> {
198	options: TransactionPoolOptions,
199	is_validator: IsValidator,
200	prometheus: Option<&'a PrometheusRegistry>,
201	client: Arc<Client>,
202	spawner: Box<dyn SpawnEssentialNamed>,
203	_phantom: PhantomData<(Client, Block)>,
204}
205
206impl<'a, Client, Block> Builder<'a, Block, Client>
207where
208	Block: BlockT,
209	Client: ClientForTransactionPool<Block>
210		+ sc_client_api::ExecutorProvider<Block>
211		+ sc_client_api::UsageProvider<Block>,
212	<Block as BlockT>::Hash: std::marker::Unpin,
213{
214	/// Creates new instance of `Builder`
215	pub fn new(
216		spawner: impl SpawnEssentialNamed + 'static,
217		client: Arc<Client>,
218		is_validator: IsValidator,
219	) -> Builder<'a, Block, Client> {
220		Builder {
221			options: Default::default(),
222			_phantom: Default::default(),
223			spawner: Box::new(spawner),
224			client,
225			is_validator,
226			prometheus: None,
227		}
228	}
229
230	/// Sets the options used for creating a transaction pool instance.
231	pub fn with_options(mut self, options: TransactionPoolOptions) -> Self {
232		self.options = options;
233		self
234	}
235
236	/// Sets the prometheus endpoint used in a transaction pool instance.
237	pub fn with_prometheus(mut self, prometheus: Option<&'a PrometheusRegistry>) -> Self {
238		self.prometheus = prometheus;
239		self
240	}
241
242	/// Creates an instance of transaction pool.
243	pub fn build(self) -> Arc<TransactionPoolHandle<Block>> {
244		tracing::info!(
245			target: LOG_TARGET,
246			txpool_type = ?self.options.txpool_type,
247			ready = ?self.options.options.ready,
248			future = ?self.options.options.future,
249			"Creating transaction pool"
250		);
251		match self.options.txpool_type {
252			TransactionPoolType::SingleState => Arc::new(SingleStateFullPool::new_full(
253				self.options.options,
254				self.is_validator,
255				self.prometheus,
256				self.spawner,
257				self.client,
258			)),
259			TransactionPoolType::ForkAware => Arc::new(ForkAwareFullPool::new_full(
260				self.options.options,
261				self.is_validator,
262				self.prometheus,
263				self.spawner,
264				self.client,
265			)),
266		}
267	}
268}