sc_transaction_pool/fork_aware_txpool/mod.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//! Substrate fork aware transaction pool implementation.
20//!
21//! # Top level overview.
22//! This documentation provides high level overview of the main structures and the main flows within
23//! the fork-aware transaction pool.
24//!
25//! ## Structures.
26//! ### View.
27//! #### Purpose.
28//! The main responsibility of the [`View`] is to provide the valid set of ready transactions at
29//! the given block. [`ForkAwareTxPool`] keeps the number of recent views for all the blocks
30//! notified since recently finalized block.
31//!
32//! The views associated with blocks at the tips of the forks are actively updated with all newly
33//! incoming transactions, while intermediate views are not updated (they still provide transactions
34//! ready to be included at that block) due to performance reasons, since every transaction
35//! submitted to the view needs to be [validated][runtime_api::validate].
36//! Building upon the older blocks happens relatively rare so this does not affect blocks filling.
37//!
38//! The view is wrapper around [`Pool`] and exposes its functionality, including the ability
39//! of [tracking][`Watcher`] the progress of every transaction.
40//!
41//! #### Views: active, inactive.
42//! All the views are stored in [`ViewStore`] structure. In this documentation the views at the tips
43//! of the forks are referred as [`active_views`], while the intermediate views as
44//! [`inactive_views`].
45//!
46//!
47//! #### The life cycle of the [`View`].
48//! Views are created when the new [`ChainEvent`] is notified to the pool. The view that is
49//! [closest][find_best_view] to the newly notified block is chosen to clone from. Once built and
50//! updated the newly created view is placed in [`active_views`]. Detailed description of view
51//! creation is described in [the material to follow](#handling-the-new-best-block). When the view
52//! is no longer at the tip of the forks, it is moved to the [`inactive_views`]. When the block
53//! number of the view is lower then the finalized block, the view is permanently removed.
54//!
55//!
56//! *Example*:
57//! The following chain:
58//! ```text
59//! C2 - C3 - C4
60//! /
61//! B1
62//! \
63//! B2 - B3 - B4
64//! ```
65//! and the following set of events:
66//! ```text
67//! New best block: B1, C3, C4, B4
68//! ```
69//! will result in the following set of views within the [`ViewStore`]:
70//! ```text
71//! active: C4, B4
72//! inactive: B1, C3
73//! ```
74//! Please note that views are only created for the notified blocks.
75//!
76//!
77//! ### View store.
78//! [`ViewStore`] is the helper structure that provides means to perform some actions like
79//! [`submit`] or [`submit_and_watch`] on every view. It keeps track of both active and inactive
80//! views.
81//!
82//! It also keeps tracks of the `most_recent_view` which is used to implement some methods of
83//! [TransactionPool API], see [API considerations](#api-considerations) section.
84//!
85//! ### Multi-view listeners
86//! There is a number of event streams that are provided by individual views:
87//! - aggregated stream of [transactions statuses][`AggregatedStream`] for all the transactions
88//! within the view in the form of `(transaction-hash, status)` tuple,
89//! - [ready notification][`vp::import_notification_stream`] (see [networking
90//! section](#networking)),
91//! - [dropped notification][`create_dropped_by_limits_stream`].
92//!
93//! These streams need to be merged into a single stream exposed by transaction pool (or used
94//! internally). Those aggregators are often referred as multi-view listeners and they implement
95//! stream-specific or event-specific logic.
96//!
97//! The most important is [`MultiViewListener`] which is owned by view store. Some internal details
98//! on events' flow is provided in [transaction status](#monitoring-the-status-of-a-transaction)
99//! section.
100//!
101//! ### Intermediate transactions buffer: [`TxMemPool`]
102//! The main purpose of an internal [`TxMemPool`] (referred to as *mempool*) is to prevent a
103//! transaction from being lost, e.g. due to race condition when the new transaction submission
104//! occurs just before the new view is created. This could also happen when a transaction is invalid
105//! on one fork and could be valid on another which is not yet fully processed by the maintain
106//! procedure. Additionally, it allows the pool to accept transactions when no blocks have been
107//! reported yet.
108//!
109//! The *mempool* keeps a track on how the transaction was submitted - keeping number of watched and
110//! non-watched transactions is useful for testing and metrics. The [transaction
111//! source][`TransactionSource`] used to submit transactions also needs to be kept in the *mempool*.
112//! The *mempool* transaction is a simple [wrapper][`TxInMemPool`] around the [`Arc`] reference to
113//! the actual extrinsic body.
114//!
115//! Once the view is created, all transactions from *mempool* are submitted to and validated at this
116//! view.
117//!
118//! The *mempool* removes its transactions when they get finalized. The transactions in *mempool*
119//! are also periodically verified at every finalized block and removed from the *mempool* if no
120//! longer valid. This is process is called [*mempool* revalidation](#mempool-pruningrevalidation).
121//!
122//! ## Flows
123//!
124//! The transaction pool internally is executing numerous tasks. This includes handling submitted
125//! transactions and tracking their progress, listening to [`ChainEvent`]s and executing the
126//! maintain process, which aims to provide the set of ready transactions. On the other side
127//! transaction pool provides a [`ready_at`] future that resolves to the iterator of ready
128//! transactions. On top of that pool performs background revalidation jobs.
129//!
130//! This section provides a top level overview of all flows within the fork aware transaction pool.
131//!
132//! ### Transaction route: [`submit`][`api_submit`]
133//! This flow is simple. Transaction is added to the mempool and if it is not rejected by it (due to
134//! size limits), it is also [submitted][`submit`] into every view in [`active_views`].
135//!
136//! When the newly created view does not contain this transaction yet, it is
137//! [re-submitted][ForkAwareTxPool::update_view_with_mempool] from [`TxMemPool`] into this view.
138//!
139//! ### Transaction route: [`submit_and_watch`][`api_submit_and_watch`]
140//!
141//! The [`submit_and_watch`] function allows to submit the transaction and track its
142//! [status][`TransactionStatus`] within the pool.
143//!
144//! When a watched transaction is submitted to the pool it is added to the *mempool* with the
145//! watched flag. The external stream for the transaction is created in a [`MultiViewListener`].
146//! Then a transaction is submitted to every active [`View`] (using
147//! [`submit_many`][`View::submit_many`]). The view's [aggregated
148//! stream][`create_aggregated_stream`] was already connected to the [`MultiViewListener`] when new
149//! view was created, so no additional action is required upon the submission. The view will provide
150//! the required updates for all the transactions over this single stream.
151//!
152//!
153//! #### Monitoring the status of a transaction
154//!
155//! Transaction status monitoring and triggering events to [external
156//! listener][`TransactionStatusStreamFor`] (e.g. to RPC client) is responsibility of the
157//! [`MultiViewListener`].
158//!
159//! Every view is providing an independent aggreagated [stream][`create_aggregated_stream`] of
160//! events for all transactions in this view, which needs to be merged into the single stream
161//! exposed to the [external listener][`TransactionStatusStreamFor`] (e.g. to RPC client). For
162//! majority of events simple forwarding would not work (e.g. we could get multiple [`Ready`]
163//! events, or [`Ready`] / [`Future`] mix). Some additional stateful logic (implemented by
164//! [`MultiViewListener`]) is required to filter and process the views' events.
165//!
166//! It is not possible to trigger some external events (e.g., [`Dropped`], [`Finalized`],
167//! [`Invalid`], and [`Broadcast`]) using only the view-aggregated streams. These events require a
168//! pool-wide understanding of the transaction state. For example, dropping a transaction from a
169//! single view does not mean it was dropped from other views. Broadcast and finalized notifications
170//! are sent to the transaction pool API, not at the view level. These events are simply ignored
171//! when they originate in the view. The pool uses a dedicated side channel exposed by
172//! [`MultiViewListener`] to trigger the beforementioned events.
173//!
174//! ### Maintain
175//! The transaction pool exposes the [task][`notification_future`] that listens to the
176//! finalized and best block streams and executes the [`maintain`] procedure.
177//!
178//! The [`maintain`] is the main procedure of the transaction pool. It handles incoming
179//! [`ChainEvent`]s, as described in the following two sub-sections.
180//!
181//! #### Handling the new (best) block
182//! If the new block actually needs to be handled, the following steps are
183//! executed:
184//! - [find][find_best_view] the best view and clone it to [create a new
185//! view][crate::ForkAwareTxPool::build_new_view],
186//! - [update the view][ForkAwareTxPool::update_view_with_mempool] with the transactions from the
187//! *mempool*
188//! - all transactions from the *mempool* (with some obvious filtering applied) are submitted to
189//! the view,
190//! - the new [aggregated stream][`create_aggregated_stream`] of all transactions statuses is
191//! created for the new view and it is connected to the multi-view-listener,
192//! - [update the view][ForkAwareTxPool::update_view_with_fork] with the transactions from the [tree
193//! route][`TreeRoute`] (which is computed from the recent best block to newly notified one by
194//! [enactment state][`EnactmentState`] helper):
195//! - resubmit the transactions from the retracted blocks,
196//! - prune extrinsic from the enacted blocks, and trigger [`InBlock`] events,
197//! - insert the newly created and updated view into the view store.
198//!
199//!
200//! #### Handling the finalized block
201//! The following actions are taken on every finalized block:
202//! - send [`Finalized`] events for every transactions on the finalized [tree route][`TreeRoute`],
203//! - remove all the views (both active and inactive) that are lower then finalized block from the
204//! view store,
205//! - removal of finalized transaction from the *mempool*,
206//! - trigger [*mempool* background revalidation](#mempool-pruningrevalidation).
207//! - clean up of multi-view listeners which is required to avoid ever-growing structures,
208//!
209//! ### Light maintain
210//! The [maintain](#maintain) procedure can sometimes be quite heavy, and it may not be accomplished
211//! within the time window expected by the block builder. On top of that block builder may want to
212//! build few blocks in the row, not giving the pool enough time to accomplish possible ongoing
213//! maintain process.
214//!
215//! To address this, there is a [light version][`ready_at_light`] of the maintain procedure. It
216//! [finds the first descendent view][`find_view_descendent_up_to_number`] up to the recent
217//! finalized block, clones it and prunes all the transactions that were included in enacted part of
218//! the traversed route, from the base view to the block at which a ready iterator was requested. No
219//! new [transaction validations][runtime_api::validate] are required to accomplish it. If no view
220//! is found, it will return the ready transactions of the most recent view processed by the
221//! transaction pool.
222//!
223//! ### Providing ready transactions: `ready_at`
224//! The asynchronous [`ready_at`] function resolves to the [ready transactions
225//! iterator][`ReadyTransactions`]. The block builder shall wait either for the future to be
226//! resolved or for timeout to be hit. To avoid building empty blocks in case of timeout, the
227//! waiting for timeout functionality was moved into the transaction pool, and new API function was
228//! added: [`ready_at_with_timeout`]. This function also provides a fall back ready iterator which
229//! is result of [light maintain](#light-maintain).
230//!
231//! New function internally waits either for [maintain](#maintain) process triggered for requested
232//! block to be accomplished or for the timeout. If timeout hits then the result of [light
233//! maintain](#light-maintain) is returned. Light maintain is always executed at the beginning of
234//! [`ready_at_with_timeout`] to make sure that it is available w/ o additional delay.
235//!
236//! If the maintain process for the requested block was accomplished before the `ready_at` functions
237//! are called both of them immediately provide the ready transactions iterator (which is simply
238//! requested on the appropriate instance of the [`View`]).
239//!
240//! The little [`ReadyPoll`] helper contained within [`ForkAwareTxPool`] as ([`ready_poll`])
241//! implements the futures management.
242//!
243//! ### Background tasks
244//! The [maintain](#maintain) procedure shall be as quick as possible, so heavy revalidation job is
245//! delegated to the background worker. These includes view and *mempool* revalidation which are
246//! both handled by the [`RevalidationQueue`] which simply sends revalidation requests to the
247//! background thread.
248//!
249//! #### View revalidation
250//! View revalidation is performed in the background thread. Revalidation is executed for every
251//! view. All the transaction from the view are [revalidated][`view::revalidate`].
252//!
253//! The fork-aware pool utilizes two threads to execute maintain and revalidation process
254//! exclusively, ensuring maintain performance without overlapping with revalidation.
255//!
256//! The view revalidation process is [triggered][`start_background_revalidation`] at the very end of
257//! the [maintain][`maintain`] process, and [stopped][`finish_background_revalidations`] at the
258//! very beginning of the next maintenance execution (upon the next [`ChainEvent`] reception). The
259//! results from the revalidation are immediately applied once the revalidation is
260//! [terminated][crate::fork_aware_txpool::view::View::finish_revalidation].
261//! ```text
262//! time: ---------------------->
263//! maintenance thread: M----M------M--M-M---
264//! revalidation thread: -RRRR-RR-----RR-R-RRR
265//! ```
266//!
267//! #### Mempool pruning/revalidation
268//! Transactions within *mempool* are constantly revalidated in the background. The
269//! [revalidation][`mp::revalidate`] is performed in [batches][`batch_size`], and transactions that
270//! were validated as latest, are revalidated first in the next iteration. The revalidation is
271//! triggered on every finalized block. If a transaction is found to be invalid, the [`Invalid`]
272//! event is sent and transaction is removed from the *mempool*.
273//!
274//! NOTE: There is one exception: if transaction is referenced by any view as ready, then it is
275//! removed from the *mempool*, but not removed from the view. The [`Invalid`] event is not sent.
276//! This case is not likely to happen, however it may need some extra attention.
277//!
278//! ### Networking
279//! The pool is exposing [`ImportNotificationStream`][`import_notification_stream`], the dedicated
280//! channel over which all ready transactions are notified. Internally this channel needs to merge
281//! all ready events from every view. This functionality is implemented by
282//! [`MultiViewImportNotificationSink`].
283//!
284//! The networking module is utilizing this channel to receive info about new ready transactions
285//! which later will be propagated over the network. On the other side, when a transaction is
286//! received networking submits transaction to the pool using [`submit`][`api_submit`].
287//!
288//! ### Handling invalid transactions
289//! Refer to *mempool* revalidation [section](#mempool-pruningrevalidation).
290//!
291//! ## Pool limits
292//! Every [`View`] has the [limits][`Options`] for the number or size of transactions it can hold.
293//! Obviously the number of transactions in every view is not distributed equally, so some views
294//! might be fully filled while others not.
295//!
296//! On the other hand the size of internal *mempool* shall also be capped, but transactions that are
297//! still referenced by views should not be removed.
298//!
299//! When the [`View`] is at its limits, it can either reject the transaction during
300//! submission process, or it can accept the transaction and drop different transaction which is
301//! already in the pool during the [`enforce_limits`][`vp::enforce_limits`] process.
302//!
303//! The [`StreamOfDropped`] stream aggregating [per-view][`create_dropped_by_limits_stream`] streams
304//! allows to monitor the transactions that were dropped by all the views (or dropped by some views
305//! while not referenced by the others), what means that transaction can also be
306//! [removed][`dropped_monitor_task`] from the *mempool*.
307//!
308//!
309//! ## API Considerations
310//! Refer to github issue: <https://github.com/paritytech/polkadot-sdk/issues/5491>
311//!
312//! [`View`]: crate::fork_aware_txpool::view::View
313//! [`view::revalidate`]: crate::fork_aware_txpool::view::View::revalidate
314//! [`start_background_revalidation`]: crate::fork_aware_txpool::view::View::start_background_revalidation
315//! [`View::submit_many`]: crate::fork_aware_txpool::view::View::submit_many
316//! [`ViewStore`]: crate::fork_aware_txpool::view_store::ViewStore
317//! [`finish_background_revalidations`]: crate::fork_aware_txpool::view_store::ViewStore::finish_background_revalidations
318//! [find_best_view]: crate::fork_aware_txpool::view_store::ViewStore::find_best_view
319//! [`find_view_descendent_up_to_number`]: crate::fork_aware_txpool::view_store::ViewStore::find_view_descendent_up_to_number
320//! [`active_views`]: crate::fork_aware_txpool::view_store::ViewStore::active_views
321//! [`inactive_views`]: crate::fork_aware_txpool::view_store::ViewStore::inactive_views
322//! [`TxMemPool`]: crate::fork_aware_txpool::tx_mem_pool::TxMemPool
323//! [`mp::revalidate`]: crate::fork_aware_txpool::tx_mem_pool::TxMemPool::revalidate
324//! [`batch_size`]: crate::fork_aware_txpool::tx_mem_pool::TXMEMPOOL_MAX_REVALIDATION_BATCH_SIZE
325//! [`TxInMemPool`]: crate::fork_aware_txpool::tx_mem_pool::TxInMemPool
326//! [`MultiViewListener`]: crate::fork_aware_txpool::multi_view_listener::MultiViewListener
327//! [`Pool`]: crate::graph::Pool
328//! [`Watcher`]: crate::graph::watcher::Watcher
329//! [`AggregatedStream`]: crate::fork_aware_txpool::view::AggregatedStream
330//! [`Options`]: crate::graph::Options
331//! [`vp::import_notification_stream`]: ../graph/validated_pool/struct.ValidatedPool.html#method.import_notification_stream
332//! [`vp::enforce_limits`]: ../graph/validated_pool/struct.ValidatedPool.html#method.enforce_limits
333//! [`create_dropped_by_limits_stream`]: ../graph/validated_pool/struct.ValidatedPool.html#method.create_dropped_by_limits_stream
334//! [`create_aggregated_stream`]: ../graph/validated_pool/struct.ValidatedPool.html#method.create_aggregated_stream
335//! [`ChainEvent`]: sc_transaction_pool_api::ChainEvent
336//! [`TransactionStatusStreamFor`]: sc_transaction_pool_api::TransactionStatusStreamFor
337//! [`api_submit`]: sc_transaction_pool_api::TransactionPool::submit_at
338//! [`api_submit_and_watch`]: sc_transaction_pool_api::TransactionPool::submit_and_watch
339//! [`ready_at_with_timeout`]: sc_transaction_pool_api::TransactionPool::ready_at_with_timeout
340//! [`TransactionSource`]: sc_transaction_pool_api::TransactionSource
341//! [TransactionPool API]: sc_transaction_pool_api::TransactionPool
342//! [`TransactionStatus`]:sc_transaction_pool_api::TransactionStatus
343//! [`Ready`]:sc_transaction_pool_api::TransactionStatus::Ready
344//! [`Future`]:sc_transaction_pool_api::TransactionStatus::Future
345//! [`Broadcast`]:sc_transaction_pool_api::TransactionStatus::Broadcast
346//! [`Invalid`]:sc_transaction_pool_api::TransactionStatus::Invalid
347//! [`InBlock`]:sc_transaction_pool_api::TransactionStatus::InBlock
348//! [`Finalized`]:sc_transaction_pool_api::TransactionStatus::Finalized
349//! [`Dropped`]:sc_transaction_pool_api::TransactionStatus::Dropped
350//! [`ReadyTransactions`]:sc_transaction_pool_api::ReadyTransactions
351//! [`dropped_monitor_task`]: ForkAwareTxPool::dropped_monitor_task
352//! [`ready_poll`]: ForkAwareTxPool::ready_poll
353//! [`ready_at_light`]: ForkAwareTxPool::ready_at_light
354//! [`ready_at`]: ../struct.ForkAwareTxPool.html#method.ready_at
355//! [`import_notification_stream`]: ../struct.ForkAwareTxPool.html#method.import_notification_stream
356//! [`maintain`]: ../struct.ForkAwareTxPool.html#method.maintain
357//! [`submit`]: ../struct.ForkAwareTxPool.html#method.submit_at
358//! [`submit_and_watch`]: ../struct.ForkAwareTxPool.html#method.submit_and_watch
359//! [`ReadyPoll`]: ../fork_aware_txpool/fork_aware_txpool/struct.ReadyPoll.html
360//! [`TreeRoute`]: sp_blockchain::TreeRoute
361//! [runtime_api::validate]: sp_transaction_pool::runtime_api::TaggedTransactionQueue::validate_transaction
362//! [`notification_future`]: crate::common::notification_future
363//! [`EnactmentState`]: crate::common::enactment_state::EnactmentState
364//! [`MultiViewImportNotificationSink`]: crate::fork_aware_txpool::import_notification_sink::MultiViewImportNotificationSink
365//! [`RevalidationQueue`]: crate::fork_aware_txpool::revalidation_worker::RevalidationQueue
366//! [`StreamOfDropped`]: crate::fork_aware_txpool::dropped_watcher::StreamOfDropped
367//! [`Arc`]: std::sync::Arc
368
369mod dropped_watcher;
370pub(crate) mod fork_aware_txpool;
371mod import_notification_sink;
372mod metrics;
373mod multi_view_listener;
374mod revalidation_worker;
375mod tx_mem_pool;
376mod view;
377mod view_store;
378
379pub use fork_aware_txpool::{ForkAwareTxPool, ForkAwareTxPoolTask};
380
381mod stream_map_util {
382 use futures::Stream;
383 use std::marker::Unpin;
384 use tokio_stream::StreamMap;
385
386 pub async fn next_event<K, V>(
387 stream_map: &mut StreamMap<K, V>,
388 ) -> Option<(K, <V as Stream>::Item)>
389 where
390 K: Clone + Unpin,
391 V: Stream + Unpin,
392 {
393 if stream_map.is_empty() {
394 // yield pending to prevent busy-loop on an empty map
395 futures::pending!()
396 }
397
398 futures::StreamExt::next(stream_map).await
399 }
400}