reth_transaction_pool/
lib.rs

1//! Reth's transaction pool implementation.
2//!
3//! This crate provides a generic transaction pool implementation.
4//!
5//! ## Functionality
6//!
7//! The transaction pool is responsible for
8//!
9//!    - recording incoming transactions
10//!    - providing existing transactions
11//!    - ordering and providing the best transactions for block production
12//!    - monitoring memory footprint and enforce pool size limits
13//!    - storing blob data for transactions in a separate blobstore on insertion
14//!
15//! ## Assumptions
16//!
17//! ### Transaction type
18//!
19//! The pool expects certain ethereum related information from the generic transaction type of the
20//! pool ([`PoolTransaction`]), this includes gas price, base fee (EIP-1559 transactions), nonce
21//! etc. It makes no assumptions about the encoding format, but the transaction type must report its
22//! size so pool size limits (memory) can be enforced.
23//!
24//! ### Transaction ordering
25//!
26//! The pending pool contains transactions that can be mined on the current state.
27//! The order in which they're returned are determined by a `Priority` value returned by the
28//! `TransactionOrdering` type this pool is configured with.
29//!
30//! This is only used in the _pending_ pool to yield the best transactions for block production. The
31//! _base pool_ is ordered by base fee, and the _queued pool_ by current distance.
32//!
33//! ### Validation
34//!
35//! The pool itself does not validate incoming transactions, instead this should be provided by
36//! implementing `TransactionsValidator`. Only transactions that the validator returns as valid are
37//! included in the pool. It is assumed that transaction that are in the pool are either valid on
38//! the current state or could become valid after certain state changes. Transactions that can never
39//! become valid (e.g. nonce lower than current on chain nonce) will never be added to the pool and
40//! instead are discarded right away.
41//!
42//! ### State Changes
43//!
44//! Once a new block is mined, the pool needs to be updated with a changeset in order to:
45//!
46//!   - remove mined transactions
47//!   - update using account changes: balance changes
48//!   - base fee updates
49//!
50//! ## Implementation details
51//!
52//! The `TransactionPool` trait exposes all externally used functionality of the pool, such as
53//! inserting, querying specific transactions by hash or retrieving the best transactions.
54//! In addition, it enables the registration of event listeners that are notified of state changes.
55//! Events are communicated via channels.
56//!
57//! ### Architecture
58//!
59//! The final `TransactionPool` is made up of two layers:
60//!
61//! The lowest layer is the actual pool implementations that manages (validated) transactions:
62//! [`TxPool`](crate::pool::txpool::TxPool). This is contained in a higher level pool type that
63//! guards the low level pool and handles additional listeners or metrics: [`PoolInner`].
64//!
65//! The transaction pool will be used by separate consumers (RPC, P2P), to make sharing easier, the
66//! [`Pool`] type is just an `Arc` wrapper around `PoolInner`. This is the usable type that provides
67//! the `TransactionPool` interface.
68//!
69//!
70//! ## Blob Transactions
71//!
72//! Blob transaction can be quite large hence they are stored in a separate blobstore. The pool is
73//! responsible for inserting blob data for new transactions into the blobstore.
74//! See also [`ValidTransaction`](validate::ValidTransaction)
75//!
76//!
77//! ## Examples
78//!
79//! Listen for new transactions and print them:
80//!
81//! ```
82//! use reth_chainspec::MAINNET;
83//! use reth_storage_api::StateProviderFactory;
84//! use reth_tasks::TokioTaskExecutor;
85//! use reth_chainspec::ChainSpecProvider;
86//! use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool, TransactionPool};
87//! use reth_transaction_pool::blobstore::InMemoryBlobStore;
88//! use reth_chainspec::EthereumHardforks;
89//! async fn t<C>(client: C)  where C: ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory + Clone + 'static{
90//!     let blob_store = InMemoryBlobStore::default();
91//!     let pool = Pool::eth_pool(
92//!         TransactionValidationTaskExecutor::eth(client, blob_store.clone(), TokioTaskExecutor::default()),
93//!         blob_store,
94//!         Default::default(),
95//!     );
96//!   let mut transactions = pool.pending_transactions_listener();
97//!   tokio::task::spawn( async move {
98//!      while let Some(tx) = transactions.recv().await {
99//!          println!("New transaction: {:?}", tx);
100//!      }
101//!   });
102//!
103//!   // do something useful with the pool, like RPC integration
104//!
105//! # }
106//! ```
107//!
108//! Spawn maintenance task to keep the pool updated
109//!
110//! ```
111//! use futures_util::Stream;
112//! use reth_chain_state::CanonStateNotification;
113//! use reth_chainspec::{MAINNET, ChainSpecProvider, ChainSpec};
114//! use reth_storage_api::{BlockReaderIdExt, StateProviderFactory};
115//! use reth_tasks::TokioTaskExecutor;
116//! use reth_tasks::TaskSpawner;
117//! use reth_tasks::TaskManager;
118//! use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool};
119//! use reth_transaction_pool::blobstore::InMemoryBlobStore;
120//! use reth_transaction_pool::maintain::{maintain_transaction_pool_future};
121//!
122//!  async fn t<C, St>(client: C, stream: St)
123//!    where C: StateProviderFactory + BlockReaderIdExt + ChainSpecProvider<ChainSpec = ChainSpec> + Clone + 'static,
124//!     St: Stream<Item = CanonStateNotification> + Send + Unpin + 'static,
125//!     {
126//!     let blob_store = InMemoryBlobStore::default();
127//!     let rt = tokio::runtime::Runtime::new().unwrap();
128//!     let manager = TaskManager::new(rt.handle().clone());
129//!     let executor = manager.executor();
130//!     let pool = Pool::eth_pool(
131//!         TransactionValidationTaskExecutor::eth(client.clone(), blob_store.clone(), executor.clone()),
132//!         blob_store,
133//!         Default::default(),
134//!     );
135//!
136//!   // spawn a task that listens for new blocks and updates the pool's transactions, mined transactions etc..
137//!   tokio::task::spawn(maintain_transaction_pool_future(client, pool, stream, executor.clone(), Default::default()));
138//!
139//! # }
140//! ```
141//!
142//! ## Feature Flags
143//!
144//! - `serde` (default): Enable serde support
145//! - `test-utils`: Export utilities for testing
146
147#![doc(
148    html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
149    html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
150    issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
151)]
152#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
153#![cfg_attr(not(test), warn(unused_crate_dependencies))]
154
155pub use crate::{
156    blobstore::{BlobStore, BlobStoreError},
157    config::{
158        LocalTransactionConfig, PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP,
159        DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS, MAX_NEW_PENDING_TXS_NOTIFICATIONS,
160        REPLACE_BLOB_PRICE_BUMP, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
161        TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
162    },
163    error::PoolResult,
164    ordering::{CoinbaseTipOrdering, Priority, TransactionOrdering},
165    pool::{
166        blob_tx_priority, fee_delta, state::SubPool, AllTransactionsEvents, FullTransactionEvent,
167        NewTransactionEvent, TransactionEvent, TransactionEvents, TransactionListenerKind,
168    },
169    traits::*,
170    validate::{
171        EthTransactionValidator, TransactionValidationOutcome, TransactionValidationTaskExecutor,
172        TransactionValidator, ValidPoolTransaction,
173    },
174};
175use crate::{identifier::TransactionId, pool::PoolInner};
176use alloy_eips::eip4844::{BlobAndProofV1, BlobTransactionSidecar};
177use alloy_primitives::{Address, TxHash, B256, U256};
178use aquamarine as _;
179use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
180use reth_eth_wire_types::HandleMempoolData;
181use reth_execution_types::ChangedAccount;
182use reth_primitives_traits::{Block, Recovered};
183use reth_storage_api::StateProviderFactory;
184use std::{collections::HashSet, sync::Arc};
185use tokio::sync::mpsc::Receiver;
186use tracing::{instrument, trace};
187
188pub mod error;
189pub mod maintain;
190pub mod metrics;
191pub mod noop;
192pub mod pool;
193pub mod validate;
194
195pub mod blobstore;
196mod config;
197pub mod identifier;
198mod ordering;
199mod traits;
200
201#[cfg(any(test, feature = "test-utils"))]
202/// Common test helpers for mocking a pool
203pub mod test_utils;
204
205/// Type alias for default ethereum transaction pool
206pub type EthTransactionPool<Client, S> = Pool<
207    TransactionValidationTaskExecutor<EthTransactionValidator<Client, EthPooledTransaction>>,
208    CoinbaseTipOrdering<EthPooledTransaction>,
209    S,
210>;
211
212/// A shareable, generic, customizable `TransactionPool` implementation.
213#[derive(Debug)]
214pub struct Pool<V, T: TransactionOrdering, S> {
215    /// Arc'ed instance of the pool internals
216    pool: Arc<PoolInner<V, T, S>>,
217}
218
219// === impl Pool ===
220
221impl<V, T, S> Pool<V, T, S>
222where
223    V: TransactionValidator,
224    T: TransactionOrdering<Transaction = <V as TransactionValidator>::Transaction>,
225    S: BlobStore,
226{
227    /// Create a new transaction pool instance.
228    pub fn new(validator: V, ordering: T, blob_store: S, config: PoolConfig) -> Self {
229        Self { pool: Arc::new(PoolInner::new(validator, ordering, blob_store, config)) }
230    }
231
232    /// Returns the wrapped pool.
233    pub(crate) fn inner(&self) -> &PoolInner<V, T, S> {
234        &self.pool
235    }
236
237    /// Get the config the pool was configured with.
238    pub fn config(&self) -> &PoolConfig {
239        self.inner().config()
240    }
241
242    /// Returns future that validates all transactions in the given iterator.
243    ///
244    /// This returns the validated transactions in the iterator's order.
245    async fn validate_all(
246        &self,
247        origin: TransactionOrigin,
248        transactions: impl IntoIterator<Item = V::Transaction>,
249    ) -> Vec<(TxHash, TransactionValidationOutcome<V::Transaction>)> {
250        futures_util::future::join_all(transactions.into_iter().map(|tx| self.validate(origin, tx)))
251            .await
252    }
253
254    /// Validates the given transaction
255    async fn validate(
256        &self,
257        origin: TransactionOrigin,
258        transaction: V::Transaction,
259    ) -> (TxHash, TransactionValidationOutcome<V::Transaction>) {
260        let hash = *transaction.hash();
261
262        let outcome = self.pool.validator().validate_transaction(origin, transaction).await;
263
264        (hash, outcome)
265    }
266
267    /// Number of transactions in the entire pool
268    pub fn len(&self) -> usize {
269        self.pool.len()
270    }
271
272    /// Whether the pool is empty
273    pub fn is_empty(&self) -> bool {
274        self.pool.is_empty()
275    }
276
277    /// Returns whether or not the pool is over its configured size and transaction count limits.
278    pub fn is_exceeded(&self) -> bool {
279        self.pool.is_exceeded()
280    }
281
282    /// Returns the configured blob store.
283    pub fn blob_store(&self) -> &S {
284        self.pool.blob_store()
285    }
286}
287
288impl<Client, S> EthTransactionPool<Client, S>
289where
290    Client:
291        ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory + Clone + 'static,
292    S: BlobStore,
293{
294    /// Returns a new [`Pool`] that uses the default [`TransactionValidationTaskExecutor`] when
295    /// validating [`EthPooledTransaction`]s and ords via [`CoinbaseTipOrdering`]
296    ///
297    /// # Example
298    ///
299    /// ```
300    /// use reth_chainspec::MAINNET;
301    /// use reth_storage_api::StateProviderFactory;
302    /// use reth_tasks::TokioTaskExecutor;
303    /// use reth_chainspec::ChainSpecProvider;
304    /// use reth_transaction_pool::{
305    ///     blobstore::InMemoryBlobStore, Pool, TransactionValidationTaskExecutor,
306    /// };
307    /// use reth_chainspec::EthereumHardforks;
308    /// # fn t<C>(client: C)  where C: ChainSpecProvider<ChainSpec: EthereumHardforks> + StateProviderFactory + Clone + 'static {
309    /// let blob_store = InMemoryBlobStore::default();
310    /// let pool = Pool::eth_pool(
311    ///     TransactionValidationTaskExecutor::eth(
312    ///         client,
313    ///         blob_store.clone(),
314    ///         TokioTaskExecutor::default(),
315    ///     ),
316    ///     blob_store,
317    ///     Default::default(),
318    /// );
319    /// # }
320    /// ```
321    pub fn eth_pool(
322        validator: TransactionValidationTaskExecutor<
323            EthTransactionValidator<Client, EthPooledTransaction>,
324        >,
325        blob_store: S,
326        config: PoolConfig,
327    ) -> Self {
328        Self::new(validator, CoinbaseTipOrdering::default(), blob_store, config)
329    }
330}
331
332/// implements the `TransactionPool` interface for various transaction pool API consumers.
333impl<V, T, S> TransactionPool for Pool<V, T, S>
334where
335    V: TransactionValidator,
336    <V as TransactionValidator>::Transaction: EthPoolTransaction,
337    T: TransactionOrdering<Transaction = <V as TransactionValidator>::Transaction>,
338    S: BlobStore,
339{
340    type Transaction = T::Transaction;
341
342    fn pool_size(&self) -> PoolSize {
343        self.pool.size()
344    }
345
346    fn block_info(&self) -> BlockInfo {
347        self.pool.block_info()
348    }
349
350    async fn add_transaction_and_subscribe(
351        &self,
352        origin: TransactionOrigin,
353        transaction: Self::Transaction,
354    ) -> PoolResult<TransactionEvents> {
355        let (_, tx) = self.validate(origin, transaction).await;
356        self.pool.add_transaction_and_subscribe(origin, tx)
357    }
358
359    async fn add_transaction(
360        &self,
361        origin: TransactionOrigin,
362        transaction: Self::Transaction,
363    ) -> PoolResult<TxHash> {
364        let (_, tx) = self.validate(origin, transaction).await;
365        let mut results = self.pool.add_transactions(origin, std::iter::once(tx));
366        results.pop().expect("result length is the same as the input")
367    }
368
369    async fn add_transactions(
370        &self,
371        origin: TransactionOrigin,
372        transactions: Vec<Self::Transaction>,
373    ) -> Vec<PoolResult<TxHash>> {
374        if transactions.is_empty() {
375            return Vec::new()
376        }
377        let validated = self.validate_all(origin, transactions).await;
378
379        self.pool.add_transactions(origin, validated.into_iter().map(|(_, tx)| tx))
380    }
381
382    fn transaction_event_listener(&self, tx_hash: TxHash) -> Option<TransactionEvents> {
383        self.pool.add_transaction_event_listener(tx_hash)
384    }
385
386    fn all_transactions_event_listener(&self) -> AllTransactionsEvents<Self::Transaction> {
387        self.pool.add_all_transactions_event_listener()
388    }
389
390    fn pending_transactions_listener_for(&self, kind: TransactionListenerKind) -> Receiver<TxHash> {
391        self.pool.add_pending_listener(kind)
392    }
393
394    fn blob_transaction_sidecars_listener(&self) -> Receiver<NewBlobSidecar> {
395        self.pool.add_blob_sidecar_listener()
396    }
397
398    fn new_transactions_listener_for(
399        &self,
400        kind: TransactionListenerKind,
401    ) -> Receiver<NewTransactionEvent<Self::Transaction>> {
402        self.pool.add_new_transaction_listener(kind)
403    }
404
405    fn pooled_transaction_hashes(&self) -> Vec<TxHash> {
406        self.pool.pooled_transactions_hashes()
407    }
408
409    fn pooled_transaction_hashes_max(&self, max: usize) -> Vec<TxHash> {
410        self.pooled_transaction_hashes().into_iter().take(max).collect()
411    }
412
413    fn pooled_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
414        self.pool.pooled_transactions()
415    }
416
417    fn pooled_transactions_max(
418        &self,
419        max: usize,
420    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
421        self.pool.pooled_transactions_max(max)
422    }
423
424    fn get_pooled_transaction_elements(
425        &self,
426        tx_hashes: Vec<TxHash>,
427        limit: GetPooledTransactionLimit,
428    ) -> Vec<<<V as TransactionValidator>::Transaction as PoolTransaction>::Pooled> {
429        self.pool.get_pooled_transaction_elements(tx_hashes, limit)
430    }
431
432    fn get_pooled_transaction_element(
433        &self,
434        tx_hash: TxHash,
435    ) -> Option<Recovered<<<V as TransactionValidator>::Transaction as PoolTransaction>::Pooled>>
436    {
437        self.pool.get_pooled_transaction_element(tx_hash)
438    }
439
440    fn best_transactions(
441        &self,
442    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
443        Box::new(self.pool.best_transactions())
444    }
445
446    fn best_transactions_with_attributes(
447        &self,
448        best_transactions_attributes: BestTransactionsAttributes,
449    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
450        self.pool.best_transactions_with_attributes(best_transactions_attributes)
451    }
452
453    fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
454        self.pool.pending_transactions()
455    }
456
457    fn pending_transactions_max(
458        &self,
459        max: usize,
460    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
461        self.pool.pending_transactions_max(max)
462    }
463
464    fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
465        self.pool.queued_transactions()
466    }
467
468    fn all_transactions(&self) -> AllPoolTransactions<Self::Transaction> {
469        self.pool.all_transactions()
470    }
471
472    fn remove_transactions(
473        &self,
474        hashes: Vec<TxHash>,
475    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
476        self.pool.remove_transactions(hashes)
477    }
478
479    fn remove_transactions_and_descendants(
480        &self,
481        hashes: Vec<TxHash>,
482    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
483        self.pool.remove_transactions_and_descendants(hashes)
484    }
485
486    fn remove_transactions_by_sender(
487        &self,
488        sender: Address,
489    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
490        self.pool.remove_transactions_by_sender(sender)
491    }
492
493    fn retain_unknown<A>(&self, announcement: &mut A)
494    where
495        A: HandleMempoolData,
496    {
497        self.pool.retain_unknown(announcement)
498    }
499
500    fn get(&self, tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
501        self.inner().get(tx_hash)
502    }
503
504    fn get_all(&self, txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
505        self.inner().get_all(txs)
506    }
507
508    fn on_propagated(&self, txs: PropagatedTransactions) {
509        self.inner().on_propagated(txs)
510    }
511
512    fn get_transactions_by_sender(
513        &self,
514        sender: Address,
515    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
516        self.pool.get_transactions_by_sender(sender)
517    }
518
519    fn get_pending_transactions_with_predicate(
520        &self,
521        predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
522    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
523        self.pool.pending_transactions_with_predicate(predicate)
524    }
525
526    fn get_pending_transactions_by_sender(
527        &self,
528        sender: Address,
529    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
530        self.pool.get_pending_transactions_by_sender(sender)
531    }
532
533    fn get_queued_transactions_by_sender(
534        &self,
535        sender: Address,
536    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
537        self.pool.get_queued_transactions_by_sender(sender)
538    }
539
540    fn get_highest_transaction_by_sender(
541        &self,
542        sender: Address,
543    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
544        self.pool.get_highest_transaction_by_sender(sender)
545    }
546
547    fn get_highest_consecutive_transaction_by_sender(
548        &self,
549        sender: Address,
550        on_chain_nonce: u64,
551    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
552        self.pool.get_highest_consecutive_transaction_by_sender(sender, on_chain_nonce)
553    }
554
555    fn get_transaction_by_sender_and_nonce(
556        &self,
557        sender: Address,
558        nonce: u64,
559    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
560        let transaction_id = TransactionId::new(self.pool.get_sender_id(sender), nonce);
561
562        self.inner().get_pool_data().all().get(&transaction_id).map(|tx| tx.transaction.clone())
563    }
564
565    fn get_transactions_by_origin(
566        &self,
567        origin: TransactionOrigin,
568    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
569        self.pool.get_transactions_by_origin(origin)
570    }
571
572    /// Returns all pending transactions filtered by [`TransactionOrigin`]
573    fn get_pending_transactions_by_origin(
574        &self,
575        origin: TransactionOrigin,
576    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
577        self.pool.get_pending_transactions_by_origin(origin)
578    }
579
580    fn unique_senders(&self) -> HashSet<Address> {
581        self.pool.unique_senders()
582    }
583
584    fn get_blob(
585        &self,
586        tx_hash: TxHash,
587    ) -> Result<Option<Arc<BlobTransactionSidecar>>, BlobStoreError> {
588        self.pool.blob_store().get(tx_hash)
589    }
590
591    fn get_all_blobs(
592        &self,
593        tx_hashes: Vec<TxHash>,
594    ) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecar>)>, BlobStoreError> {
595        self.pool.blob_store().get_all(tx_hashes)
596    }
597
598    fn get_all_blobs_exact(
599        &self,
600        tx_hashes: Vec<TxHash>,
601    ) -> Result<Vec<Arc<BlobTransactionSidecar>>, BlobStoreError> {
602        self.pool.blob_store().get_exact(tx_hashes)
603    }
604
605    fn get_blobs_for_versioned_hashes(
606        &self,
607        versioned_hashes: &[B256],
608    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError> {
609        self.pool.blob_store().get_by_versioned_hashes(versioned_hashes)
610    }
611}
612
613impl<V, T, S> TransactionPoolExt for Pool<V, T, S>
614where
615    V: TransactionValidator,
616    <V as TransactionValidator>::Transaction: EthPoolTransaction,
617    T: TransactionOrdering<Transaction = <V as TransactionValidator>::Transaction>,
618    S: BlobStore,
619{
620    #[instrument(skip(self), target = "txpool")]
621    fn set_block_info(&self, info: BlockInfo) {
622        trace!(target: "txpool", "updating pool block info");
623        self.pool.set_block_info(info)
624    }
625
626    fn on_canonical_state_change<B>(&self, update: CanonicalStateUpdate<'_, B>)
627    where
628        B: Block,
629    {
630        self.pool.on_canonical_state_change(update);
631    }
632
633    fn update_accounts(&self, accounts: Vec<ChangedAccount>) {
634        self.pool.update_accounts(accounts);
635    }
636
637    fn delete_blob(&self, tx: TxHash) {
638        self.pool.delete_blob(tx)
639    }
640
641    fn delete_blobs(&self, txs: Vec<TxHash>) {
642        self.pool.delete_blobs(txs)
643    }
644
645    fn cleanup_blobs(&self) {
646        self.pool.cleanup_blobs()
647    }
648}
649
650impl<V, T: TransactionOrdering, S> Clone for Pool<V, T, S> {
651    fn clone(&self) -> Self {
652        Self { pool: Arc::clone(&self.pool) }
653    }
654}