//! A transaction pool implementation that does nothing. //! //! This is useful for wiring components together that don't require an actual pool but still need //! to be generic over it. use crate::{ blobstore::BlobStoreError, error::PoolError, traits::{ BestTransactionsAttributes, GetPooledTransactionLimit, NewBlobSidecar, TransactionListenerKind, }, validate::ValidTransaction, AllPoolTransactions, AllTransactionsEvents, BestTransactions, BlockInfo, EthPoolTransaction, EthPooledTransaction, NewTransactionEvent, PoolResult, PoolSize, PoolTransaction, PooledTransactionsElement, PropagatedTransactions, TransactionEvents, TransactionOrigin, TransactionPool, TransactionValidationOutcome, TransactionValidator, ValidPoolTransaction, }; use reth_eth_wire_types::HandleMempoolData; use reth_primitives::{Address, BlobTransactionSidecar, TxHash, U256}; use std::{collections::HashSet, marker::PhantomData, sync::Arc}; use tokio::sync::{mpsc, mpsc::Receiver}; /// A [`TransactionPool`] implementation that does nothing. /// /// All transactions are rejected and no events are emitted. /// This type will never hold any transactions and is only useful for wiring components together. #[derive(Debug, Clone, Default)] #[non_exhaustive] pub struct NoopTransactionPool; impl TransactionPool for NoopTransactionPool { type Transaction = EthPooledTransaction; fn pool_size(&self) -> PoolSize { Default::default() } fn block_info(&self) -> BlockInfo { BlockInfo { last_seen_block_hash: Default::default(), last_seen_block_number: 0, pending_basefee: 0, pending_blob_fee: None, } } async fn add_transaction_and_subscribe( &self, _origin: TransactionOrigin, transaction: Self::Transaction, ) -> PoolResult { let hash = *transaction.hash(); Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction)))) } async fn add_transaction( &self, _origin: TransactionOrigin, transaction: Self::Transaction, ) -> PoolResult { let hash = *transaction.hash(); Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction)))) } async fn add_transactions( &self, _origin: TransactionOrigin, transactions: Vec, ) -> Vec> { transactions .into_iter() .map(|transaction| { let hash = *transaction.hash(); Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction)))) }) .collect() } fn transaction_event_listener(&self, _tx_hash: TxHash) -> Option { None } fn all_transactions_event_listener(&self) -> AllTransactionsEvents { AllTransactionsEvents::new(mpsc::channel(1).1) } fn pending_transactions_listener_for( &self, _kind: TransactionListenerKind, ) -> Receiver { mpsc::channel(1).1 } fn new_transactions_listener(&self) -> Receiver> { mpsc::channel(1).1 } fn blob_transaction_sidecars_listener(&self) -> Receiver { mpsc::channel(1).1 } fn new_transactions_listener_for( &self, _kind: TransactionListenerKind, ) -> Receiver> { mpsc::channel(1).1 } fn pooled_transaction_hashes(&self) -> Vec { vec![] } fn pooled_transaction_hashes_max(&self, _max: usize) -> Vec { vec![] } fn pooled_transactions(&self) -> Vec>> { vec![] } fn pooled_transactions_max( &self, _max: usize, ) -> Vec>> { vec![] } fn get_pooled_transaction_elements( &self, _tx_hashes: Vec, _limit: GetPooledTransactionLimit, ) -> Vec { vec![] } fn get_pooled_transaction_element( &self, _tx_hash: TxHash, ) -> Option { None } fn best_transactions( &self, ) -> Box>>> { Box::new(std::iter::empty()) } fn best_transactions_with_base_fee( &self, _: u64, ) -> Box>>> { Box::new(std::iter::empty()) } fn best_transactions_with_attributes( &self, _: BestTransactionsAttributes, ) -> Box>>> { Box::new(std::iter::empty()) } fn pending_transactions(&self) -> Vec>> { vec![] } fn queued_transactions(&self) -> Vec>> { vec![] } fn all_transactions(&self) -> AllPoolTransactions { AllPoolTransactions::default() } fn remove_transactions( &self, _hashes: Vec, ) -> Vec>> { vec![] } fn retain_unknown(&self, _announcement: &mut A) where A: HandleMempoolData, { } fn get(&self, _tx_hash: &TxHash) -> Option>> { None } fn get_all(&self, _txs: Vec) -> Vec>> { vec![] } fn on_propagated(&self, _txs: PropagatedTransactions) {} fn get_transactions_by_sender( &self, _sender: Address, ) -> Vec>> { vec![] } fn get_transactions_by_sender_and_nonce( &self, _sender: Address, _nonce: u64, ) -> Option>> { None } fn get_transactions_by_origin( &self, _origin: TransactionOrigin, ) -> Vec>> { vec![] } fn unique_senders(&self) -> HashSet
{ Default::default() } fn get_blob(&self, _tx_hash: TxHash) -> Result, BlobStoreError> { Ok(None) } fn get_all_blobs( &self, _tx_hashes: Vec, ) -> Result, BlobStoreError> { Ok(vec![]) } fn get_all_blobs_exact( &self, tx_hashes: Vec, ) -> Result, BlobStoreError> { if tx_hashes.is_empty() { return Ok(vec![]) } Err(BlobStoreError::MissingSidecar(tx_hashes[0])) } } /// A [`TransactionValidator`] that does nothing. #[derive(Debug, Clone)] #[non_exhaustive] pub struct MockTransactionValidator { propagate_local: bool, _marker: PhantomData, } impl TransactionValidator for MockTransactionValidator { type Transaction = T; async fn validate_transaction( &self, origin: TransactionOrigin, mut transaction: Self::Transaction, ) -> TransactionValidationOutcome { let maybe_sidecar = transaction.take_blob().maybe_sidecar().cloned(); // we return `balance: U256::MAX` to simulate a valid transaction which will never go into // overdraft TransactionValidationOutcome::Valid { balance: U256::MAX, state_nonce: 0, transaction: ValidTransaction::new(transaction, maybe_sidecar), propagate: match origin { TransactionOrigin::External => true, TransactionOrigin::Local => self.propagate_local, TransactionOrigin::Private => false, }, } } } impl MockTransactionValidator { /// Creates a new [`MockTransactionValidator`] that does not allow local transactions to be /// propagated. pub fn no_propagate_local() -> Self { Self { propagate_local: false, _marker: Default::default() } } } impl Default for MockTransactionValidator { fn default() -> Self { Self { propagate_local: true, _marker: Default::default() } } } /// An error that contains the transaction that failed to be inserted into the noop pool. #[derive(Debug, Clone, thiserror::Error)] #[error("can't insert transaction into the noop pool that does nothing")] pub struct NoopInsertError { tx: EthPooledTransaction, } impl NoopInsertError { const fn new(tx: EthPooledTransaction) -> Self { Self { tx } } /// Returns the transaction that failed to be inserted. pub fn into_inner(self) -> EthPooledTransaction { self.tx } }