Skip to main content

reth_transaction_pool/pool/
mod.rs

1//! Transaction Pool internals.
2//!
3//! Incoming transactions are validated before they enter the pool first. The validation outcome can
4//! have 3 states:
5//!
6//!  1. Transaction can _never_ be valid
7//!  2. Transaction is _currently_ valid
8//!  3. Transaction is _currently_ invalid, but could potentially become valid in the future
9//!
10//! However, (2.) and (3.) of a transaction can only be determined on the basis of the current
11//! state, whereas (1.) holds indefinitely. This means once the state changes (2.) and (3.) the
12//! state of a transaction needs to be reevaluated again.
13//!
14//! The transaction pool is responsible for storing new, valid transactions and providing the next
15//! best transactions sorted by their priority. Where priority is determined by the transaction's
16//! score ([`TransactionOrdering`]).
17//!
18//! Furthermore, the following characteristics fall under (3.):
19//!
20//!  a) Nonce of a transaction is higher than the expected nonce for the next transaction of its
21//! sender. A distinction is made here whether multiple transactions from the same sender have
22//! gapless nonce increments.
23//!
24//!  a)(1) If _no_ transaction is missing in a chain of multiple
25//! transactions from the same sender (all nonce in row), all of them can in principle be executed
26//! on the current state one after the other.
27//!
28//!  a)(2) If there's a nonce gap, then all
29//! transactions after the missing transaction are blocked until the missing transaction arrives.
30//!
31//!  b) Transaction does not meet the dynamic fee cap requirement introduced by EIP-1559: The
32//! fee cap of the transaction needs to be no less than the base fee of block.
33//!
34//!
35//! In essence the transaction pool is made of three separate sub-pools:
36//!
37//!  - Pending Pool: Contains all transactions that are valid on the current state and satisfy (3.
38//!    a)(1): _No_ nonce gaps. A _pending_ transaction is considered _ready_ when it has the lowest
39//!    nonce of all transactions from the same sender. Once a _ready_ transaction with nonce `n` has
40//!    been executed, the next highest transaction from the same sender `n + 1` becomes ready.
41//!
42//!  - Queued Pool: Contains all transactions that are currently blocked by missing transactions:
43//!    (3. a)(2): _With_ nonce gaps or due to lack of funds.
44//!
45//!  - Basefee Pool: To account for the dynamic base fee requirement (3. b) which could render an
46//!    EIP-1559 and all subsequent transactions of the sender currently invalid.
47//!
48//! The classification of transactions is always dependent on the current state that is changed as
49//! soon as a new block is mined. Once a new block is mined, the account changeset must be applied
50//! to the transaction pool.
51//!
52//!
53//! Depending on the use case, consumers of the [`TransactionPool`](crate::traits::TransactionPool)
54//! are interested in (2.) and/or (3.).
55
56//! A generic [`TransactionPool`](crate::traits::TransactionPool) that only handles transactions.
57//!
58//! This Pool maintains two separate sub-pools for (2.) and (3.)
59//!
60//! ## Terminology
61//!
62//!  - _Pending_: pending transactions are transactions that fall under (2.). These transactions can
63//!    currently be executed and are stored in the pending sub-pool
64//!  - _Queued_: queued transactions are transactions that fall under category (3.). Those
65//!    transactions are _currently_ waiting for state changes that eventually move them into
66//!    category (2.) and become pending.
67
68use crate::{
69    blobstore::{BlobStore, PooledBlobSidecar},
70    error::{PoolError, PoolErrorKind, PoolResult},
71    identifier::{SenderId, SenderIdentifiers, TransactionId},
72    metrics::BlobStoreMetrics,
73    pool::{
74        listener::{
75            BlobTransactionSidecarListener, PendingTransactionHashListener, PoolEventBroadcast,
76            TransactionListener,
77        },
78        state::SubPool,
79        txpool::{SenderInfo, TxPool},
80        update::UpdateOutcome,
81    },
82    traits::{
83        AllPoolTransactions, BestTransactionsAttributes, BlockInfo, GetPooledTransactionLimit,
84        NewBlobSidecar, PoolSize, PoolTransaction, PropagatedTransactions, TransactionOrigin,
85    },
86    validate::{TransactionValidationOutcome, ValidPoolTransaction, ValidTransaction},
87    CanonicalStateUpdate, EthPoolTransaction, PoolConfig, TransactionOrdering,
88    TransactionValidator,
89};
90
91use alloy_primitives::{
92    map::{AddressSet, HashSet},
93    Address, TxHash, B256,
94};
95use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
96use reth_eth_wire_types::HandleMempoolData;
97use reth_execution_types::ChangedAccount;
98
99use alloy_eips::{eip7594::BlobTransactionSidecarVariant, Typed2718};
100use reth_primitives_traits::Recovered;
101use rustc_hash::FxHashMap;
102use std::{
103    fmt,
104    sync::{
105        atomic::{AtomicBool, Ordering},
106        Arc,
107    },
108    time::Instant,
109};
110use tokio::sync::mpsc;
111use tracing::{debug, trace, warn};
112mod events;
113pub use best::{BestTransactionFilter, BestTransactionsWithPrioritizedSenders};
114pub use blob::{blob_tx_priority, fee_delta, BlobOrd, BlobTransactions};
115pub use events::{FullTransactionEvent, NewTransactionEvent, TransactionEvent};
116pub use listener::{AllTransactionsEvents, TransactionEvents, TransactionListenerKind};
117pub use parked::{BasefeeOrd, ParkedOrd, ParkedPool, QueuedOrd};
118pub use pending::PendingPool;
119
120mod best;
121pub use best::BestTransactions;
122
123mod blob;
124pub mod listener;
125mod parked;
126pub mod pending;
127pub mod size;
128pub(crate) mod state;
129pub mod txpool;
130mod update;
131
132/// Bound on number of pending transactions from `reth_network::TransactionsManager` to buffer.
133pub const PENDING_TX_LISTENER_BUFFER_SIZE: usize = 2048;
134/// Bound on number of new transactions from `reth_network::TransactionsManager` to buffer.
135pub const NEW_TX_LISTENER_BUFFER_SIZE: usize = 1024;
136
137const BLOB_SIDECAR_LISTENER_BUFFER_SIZE: usize = 512;
138
139/// Transaction pool internals.
140pub struct PoolInner<V, T, S>
141where
142    T: TransactionOrdering,
143{
144    /// Internal mapping of addresses to plain ints.
145    identifiers: RwLock<SenderIdentifiers>,
146    /// Transaction validator.
147    validator: V,
148    /// Storage for blob transactions
149    blob_store: S,
150    /// The internal pool that manages all transactions.
151    pool: RwLock<TxPool<T>>,
152    /// Pool settings.
153    config: PoolConfig,
154    /// Manages listeners for transaction state change events.
155    event_listener: RwLock<PoolEventBroadcast<T::Transaction>>,
156    /// Tracks whether any event listeners have ever been installed.
157    has_event_listeners: AtomicBool,
158    /// Listeners for new _full_ pending transactions.
159    pending_transaction_listener: RwLock<Vec<PendingTransactionHashListener>>,
160    /// Listeners for new transactions added to the pool.
161    transaction_listener: RwLock<Vec<TransactionListener<T::Transaction>>>,
162    /// Listener for new blob transaction sidecars added to the pool.
163    blob_transaction_sidecar_listener: Mutex<Vec<BlobTransactionSidecarListener>>,
164    /// Metrics for the blob store
165    blob_store_metrics: BlobStoreMetrics,
166}
167
168// === impl PoolInner ===
169
170impl<V, T, S> PoolInner<V, T, S>
171where
172    V: TransactionValidator,
173    T: TransactionOrdering<Transaction = <V as TransactionValidator>::Transaction>,
174    S: BlobStore,
175{
176    /// Create a new transaction pool instance.
177    pub fn new(validator: V, ordering: T, blob_store: S, config: PoolConfig) -> Self {
178        Self {
179            identifiers: Default::default(),
180            validator,
181            event_listener: Default::default(),
182            has_event_listeners: AtomicBool::new(false),
183            pool: RwLock::new(TxPool::new(ordering, config.clone())),
184            pending_transaction_listener: Default::default(),
185            transaction_listener: Default::default(),
186            blob_transaction_sidecar_listener: Default::default(),
187            config,
188            blob_store,
189            blob_store_metrics: Default::default(),
190        }
191    }
192
193    /// Returns the configured blob store.
194    pub const fn blob_store(&self) -> &S {
195        &self.blob_store
196    }
197
198    /// Returns stats about the size of the pool.
199    pub fn size(&self) -> PoolSize {
200        self.get_pool_data().size()
201    }
202
203    /// Returns the currently tracked block
204    pub fn block_info(&self) -> BlockInfo {
205        self.get_pool_data().block_info()
206    }
207    /// Sets the currently tracked block.
208    ///
209    /// This will also notify subscribers about any transactions that were promoted to the pending
210    /// pool due to fee changes.
211    pub fn set_block_info(&self, info: BlockInfo) {
212        let outcome = self.pool.write().set_block_info(info);
213
214        // Notify subscribers about promoted transactions due to fee changes
215        self.notify_on_transaction_updates(outcome.promoted, outcome.discarded);
216    }
217
218    /// Returns the internal [`SenderId`] for this address, allocating a new mapping when the
219    /// address is first observed.
220    ///
221    /// This must only be used on paths that intentionally begin tracking a sender, such as
222    /// transaction insertion. Read-only lookups should prefer [`Self::sender_id`] to avoid
223    /// growing the sender-id map for unknown addresses.
224    pub fn get_sender_id(&self, addr: Address) -> SenderId {
225        self.identifiers.write().sender_id_or_create(addr)
226    }
227
228    /// Returns the internal [`SenderId`] for this address if it is already tracked.
229    ///
230    /// Unlike [`Self::get_sender_id`], this never allocates a new sender mapping and is therefore
231    /// suitable for read-only queries or best-effort cleanup on unknown addresses.
232    pub fn sender_id(&self, addr: &Address) -> Option<SenderId> {
233        self.identifiers.read().sender_id(addr)
234    }
235
236    /// Returns the internal [`SenderId`]s for the given addresses.
237    pub fn get_sender_ids(&self, addrs: impl IntoIterator<Item = Address>) -> Vec<SenderId> {
238        self.identifiers.write().sender_ids_or_create(addrs)
239    }
240
241    /// Returns all senders in the pool
242    pub fn unique_senders(&self) -> AddressSet {
243        self.get_pool_data().unique_senders()
244    }
245
246    /// Converts the changed accounts to a map of sender ids to sender info (internal identifier
247    /// used for __tracked__ accounts)
248    fn changed_senders(
249        &self,
250        accs: impl Iterator<Item = ChangedAccount>,
251    ) -> FxHashMap<SenderId, SenderInfo> {
252        let identifiers = self.identifiers.read();
253        accs.into_iter()
254            .filter_map(|acc| {
255                let ChangedAccount { address, nonce, balance } = acc;
256                let sender_id = identifiers.sender_id(&address)?;
257                Some((sender_id, SenderInfo { state_nonce: nonce, balance }))
258            })
259            .collect()
260    }
261
262    /// Get the config the pool was configured with.
263    pub const fn config(&self) -> &PoolConfig {
264        &self.config
265    }
266
267    /// Get the validator reference.
268    pub const fn validator(&self) -> &V {
269        &self.validator
270    }
271
272    /// Adds a new transaction listener to the pool that gets notified about every new _pending_
273    /// transaction inserted into the pool
274    pub fn add_pending_listener(&self, kind: TransactionListenerKind) -> mpsc::Receiver<TxHash> {
275        let (sender, rx) = mpsc::channel(self.config.pending_tx_listener_buffer_size);
276        let listener = PendingTransactionHashListener { sender, kind };
277
278        let mut listeners = self.pending_transaction_listener.write();
279        // Clean up dead listeners before adding new one
280        listeners.retain(|l| !l.sender.is_closed());
281        listeners.push(listener);
282
283        rx
284    }
285
286    /// Adds a new transaction listener to the pool that gets notified about every new transaction.
287    pub fn add_new_transaction_listener(
288        &self,
289        kind: TransactionListenerKind,
290    ) -> mpsc::Receiver<NewTransactionEvent<T::Transaction>> {
291        let (sender, rx) = mpsc::channel(self.config.new_tx_listener_buffer_size);
292        let listener = TransactionListener { sender, kind };
293
294        let mut listeners = self.transaction_listener.write();
295        // Clean up dead listeners before adding new one
296        listeners.retain(|l| !l.sender.is_closed());
297        listeners.push(listener);
298
299        rx
300    }
301    /// Adds a new blob sidecar listener to the pool that gets notified about every new
302    /// eip4844 transaction's blob sidecar.
303    pub fn add_blob_sidecar_listener(&self) -> mpsc::Receiver<NewBlobSidecar> {
304        let (sender, rx) = mpsc::channel(BLOB_SIDECAR_LISTENER_BUFFER_SIZE);
305        let listener = BlobTransactionSidecarListener { sender };
306        self.blob_transaction_sidecar_listener.lock().push(listener);
307        rx
308    }
309
310    /// If the pool contains the transaction, this adds a new listener that gets notified about
311    /// transaction events.
312    pub fn add_transaction_event_listener(&self, tx_hash: TxHash) -> Option<TransactionEvents> {
313        if !self.get_pool_data().contains(&tx_hash) {
314            return None
315        }
316        let mut listener = self.event_listener.write();
317        let events = listener.subscribe(tx_hash);
318        self.mark_event_listener_installed();
319        Some(events)
320    }
321
322    /// Adds a listener for all transaction events.
323    pub fn add_all_transactions_event_listener(&self) -> AllTransactionsEvents<T::Transaction> {
324        let mut listener = self.event_listener.write();
325        let events = listener.subscribe_all();
326        self.mark_event_listener_installed();
327        events
328    }
329
330    #[inline]
331    fn has_event_listeners(&self) -> bool {
332        self.has_event_listeners.load(Ordering::Relaxed)
333    }
334
335    #[inline]
336    fn mark_event_listener_installed(&self) {
337        self.has_event_listeners.store(true, Ordering::Relaxed);
338    }
339
340    #[inline]
341    fn update_event_listener_state(&self, listener: &PoolEventBroadcast<T::Transaction>) {
342        if listener.is_empty() {
343            self.has_event_listeners.store(false, Ordering::Relaxed);
344        }
345    }
346
347    #[inline]
348    fn with_event_listener<F>(&self, emit: F)
349    where
350        F: FnOnce(&mut PoolEventBroadcast<T::Transaction>),
351    {
352        if !self.has_event_listeners() {
353            return
354        }
355        let mut listener = self.event_listener.write();
356        if !listener.is_empty() {
357            emit(&mut listener);
358        }
359        self.update_event_listener_state(&listener);
360    }
361
362    /// Returns a read lock to the pool's data.
363    pub fn get_pool_data(&self) -> RwLockReadGuard<'_, TxPool<T>> {
364        self.pool.read()
365    }
366
367    /// Returns transactions in the pool that can be propagated
368    pub fn pooled_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
369        let mut out = Vec::new();
370        self.append_pooled_transactions(&mut out);
371        out
372    }
373
374    /// Returns hashes of transactions in the pool that can be propagated.
375    pub fn pooled_transactions_hashes(&self) -> Vec<TxHash> {
376        let mut out = Vec::new();
377        self.append_pooled_transactions_hashes(&mut out);
378        out
379    }
380
381    /// Returns only the first `max` transactions in the pool that can be propagated.
382    pub fn pooled_transactions_max(
383        &self,
384        max: usize,
385    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
386        if max == 0 {
387            return Vec::new()
388        }
389
390        let pool = self.get_pool_data();
391        let mut out = Vec::with_capacity(max.min(pool.all().len()));
392        out.extend(pool.all().transactions_iter().filter(|tx| tx.propagate).take(max).cloned());
393        out
394    }
395
396    /// Extends the given vector with all transactions in the pool that can be propagated.
397    pub fn append_pooled_transactions(
398        &self,
399        out: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
400    ) {
401        out.extend(
402            self.get_pool_data().all().transactions_iter().filter(|tx| tx.propagate).cloned(),
403        );
404    }
405
406    /// Extends the given vector with pooled transactions for the given hashes that are allowed to
407    /// be propagated.
408    pub fn append_pooled_transaction_elements(
409        &self,
410        tx_hashes: &[TxHash],
411        limit: GetPooledTransactionLimit,
412        out: &mut Vec<<<V as TransactionValidator>::Transaction as PoolTransaction>::Pooled>,
413    ) where
414        <V as TransactionValidator>::Transaction: EthPoolTransaction,
415    {
416        let transactions = self.get_all_propagatable(tx_hashes);
417        let mut size = 0;
418        for transaction in transactions {
419            let encoded_len = transaction.encoded_length();
420            let Some(pooled) = self.to_pooled_transaction(transaction) else {
421                continue;
422            };
423
424            size += encoded_len;
425            out.push(pooled.into_inner());
426
427            if limit.exceeds(size) {
428                break
429            }
430        }
431    }
432
433    /// Extends the given vector with the hashes of all transactions in the pool that can be
434    /// propagated.
435    pub fn append_pooled_transactions_hashes(&self, out: &mut Vec<TxHash>) {
436        out.extend(
437            self.get_pool_data()
438                .all()
439                .transactions_iter()
440                .filter(|tx| tx.propagate)
441                .map(|tx| *tx.hash()),
442        );
443    }
444
445    /// Extends the given vector with only the first `max` transactions in the pool that can be
446    /// propagated.
447    pub fn append_pooled_transactions_max(
448        &self,
449        max: usize,
450        out: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
451    ) {
452        out.extend(
453            self.get_pool_data()
454                .all()
455                .transactions_iter()
456                .filter(|tx| tx.propagate)
457                .take(max)
458                .cloned(),
459        );
460    }
461
462    /// Returns only the first `max` hashes of transactions in the pool that can be propagated.
463    pub fn pooled_transactions_hashes_max(&self, max: usize) -> Vec<TxHash> {
464        if max == 0 {
465            return Vec::new();
466        }
467
468        let pool = self.get_pool_data();
469        let mut out = Vec::with_capacity(max.min(pool.all().len()));
470        out.extend(
471            pool.all().transactions_iter().filter(|tx| tx.propagate).take(max).map(|tx| *tx.hash()),
472        );
473        out
474    }
475
476    /// Converts the internally tracked transaction to the pooled format.
477    ///
478    /// If the transaction is an EIP-4844 transaction, the blob sidecar is fetched from the blob
479    /// store and attached to the transaction.
480    fn to_pooled_transaction(
481        &self,
482        transaction: Arc<ValidPoolTransaction<T::Transaction>>,
483    ) -> Option<Recovered<<<V as TransactionValidator>::Transaction as PoolTransaction>::Pooled>>
484    where
485        <V as TransactionValidator>::Transaction: EthPoolTransaction,
486    {
487        if transaction.is_eip4844() {
488            let sidecar = self.blob_store.get(*transaction.hash()).ok()??;
489            transaction.transaction.clone().try_into_pooled_eip4844(sidecar)
490        } else {
491            transaction
492                .transaction
493                .clone_into_pooled()
494                .inspect_err(|err| {
495                    debug!(
496                        target: "txpool", %err,
497                        "failed to convert transaction to pooled element; skipping",
498                    );
499                })
500                .ok()
501        }
502    }
503
504    /// Returns pooled transactions for the given transaction hashes that are allowed to be
505    /// propagated.
506    pub fn get_pooled_transaction_elements(
507        &self,
508        tx_hashes: Vec<TxHash>,
509        limit: GetPooledTransactionLimit,
510    ) -> Vec<<<V as TransactionValidator>::Transaction as PoolTransaction>::Pooled>
511    where
512        <V as TransactionValidator>::Transaction: EthPoolTransaction,
513    {
514        let mut elements = Vec::new();
515        self.append_pooled_transaction_elements(&tx_hashes, limit, &mut elements);
516        elements.shrink_to_fit();
517        elements
518    }
519
520    /// Returns converted pooled transaction for the given transaction hash.
521    pub fn get_pooled_transaction_element(
522        &self,
523        tx_hash: TxHash,
524    ) -> Option<Recovered<<<V as TransactionValidator>::Transaction as PoolTransaction>::Pooled>>
525    where
526        <V as TransactionValidator>::Transaction: EthPoolTransaction,
527    {
528        self.get(&tx_hash).and_then(|tx| self.to_pooled_transaction(tx))
529    }
530
531    /// Updates the entire pool after a new block was executed.
532    pub fn on_canonical_state_change(&self, update: CanonicalStateUpdate<'_, V::Block>) {
533        trace!(target: "txpool", ?update, "updating pool on canonical state change");
534
535        let block_info = update.block_info();
536        let CanonicalStateUpdate {
537            new_tip, changed_accounts, mined_transactions, update_kind, ..
538        } = update;
539        self.validator.on_new_head_block(new_tip);
540
541        let changed_senders = self.changed_senders(changed_accounts.into_iter());
542
543        // update the pool
544        let outcome = self.pool.write().on_canonical_state_change(
545            block_info,
546            mined_transactions,
547            changed_senders,
548            update_kind,
549        );
550
551        // This will discard outdated transactions based on the account's nonce
552        self.delete_discarded_blobs(outcome.discarded.iter());
553
554        // notify listeners about updates
555        self.notify_on_new_state(outcome);
556    }
557
558    /// Performs account updates on the pool.
559    ///
560    /// This will either promote or discard transactions based on the new account state.
561    ///
562    /// This should be invoked when the pool drifted and accounts are updated manually
563    pub fn update_accounts(&self, accounts: Vec<ChangedAccount>) {
564        let changed_senders = self.changed_senders(accounts.into_iter());
565        let UpdateOutcome { promoted, discarded } =
566            self.pool.write().update_accounts(changed_senders);
567
568        self.notify_on_transaction_updates(promoted, discarded);
569    }
570
571    /// Add a single validated transaction into the pool.
572    ///
573    /// Returns the outcome and optionally metadata to be processed after the pool lock is
574    /// released.
575    ///
576    /// Note: this is only used internally by [`Self::add_transactions()`], all new transaction(s)
577    /// come in through that function, either as a batch or `std::iter::once`.
578    fn add_transaction(
579        &self,
580        pool: &mut RwLockWriteGuard<'_, TxPool<T>>,
581        origin: TransactionOrigin,
582        tx: TransactionValidationOutcome<T::Transaction>,
583        timestamp: Instant,
584    ) -> (PoolResult<AddedTransactionOutcome>, Option<AddedTransactionMeta<T::Transaction>>) {
585        match tx {
586            TransactionValidationOutcome::Valid {
587                balance,
588                state_nonce,
589                transaction,
590                propagate,
591                bytecode_hash,
592                authorities,
593            } => {
594                let sender_id = self.get_sender_id(transaction.sender());
595                let transaction_id = TransactionId::new(sender_id, transaction.nonce());
596
597                // split the valid transaction and the blob sidecar if it has any
598                let (transaction, blob_sidecar) = match transaction {
599                    ValidTransaction::Valid(tx) => (tx, None),
600                    ValidTransaction::ValidWithSidecar { transaction, sidecar } => {
601                        debug_assert!(
602                            transaction.is_eip4844(),
603                            "validator returned sidecar for non EIP-4844 transaction"
604                        );
605                        (transaction, Some(sidecar))
606                    }
607                };
608
609                let tx = ValidPoolTransaction {
610                    transaction,
611                    transaction_id,
612                    propagate,
613                    timestamp,
614                    origin,
615                    authority_ids: authorities.map(|auths| self.get_sender_ids(auths)),
616                };
617
618                let added = match pool.add_transaction(tx, balance, state_nonce, bytecode_hash) {
619                    Ok(added) => added,
620                    Err(err) => return (Err(err), None),
621                };
622                let hash = *added.hash();
623                let state = added.transaction_state();
624
625                let meta = AddedTransactionMeta { added, blob_sidecar };
626
627                (Ok(AddedTransactionOutcome { hash, state }), Some(meta))
628            }
629            TransactionValidationOutcome::Invalid(tx, err) => {
630                self.with_event_listener(|listener| listener.invalid(tx.hash()));
631                (Err(PoolError::new(*tx.hash(), err)), None)
632            }
633            TransactionValidationOutcome::Error(tx_hash, err) => {
634                self.with_event_listener(|listener| listener.discarded(&tx_hash));
635                (Err(PoolError::other(tx_hash, err)), None)
636            }
637        }
638    }
639
640    /// Adds a transaction and returns the event stream.
641    pub fn add_transaction_and_subscribe(
642        &self,
643        origin: TransactionOrigin,
644        tx: TransactionValidationOutcome<T::Transaction>,
645    ) -> PoolResult<TransactionEvents> {
646        let listener = {
647            let mut listener = self.event_listener.write();
648            let events = listener.subscribe(tx.tx_hash());
649            self.mark_event_listener_installed();
650            events
651        };
652        let mut results = self.add_transactions(origin, std::iter::once(tx));
653        results.pop().expect("result length is the same as the input")?;
654        Ok(listener)
655    }
656
657    /// Adds all transactions in the iterator to the pool, returning a list of results.
658    ///
659    /// Convenience method that assigns the same origin to all transactions. Delegates to
660    /// [`Self::add_transactions_with_origins`].
661    pub fn add_transactions(
662        &self,
663        origin: TransactionOrigin,
664        transactions: impl IntoIterator<Item = TransactionValidationOutcome<T::Transaction>>,
665    ) -> Vec<PoolResult<AddedTransactionOutcome>> {
666        self.add_transactions_with_origins(transactions.into_iter().map(|tx| (origin, tx)))
667    }
668
669    /// Adds all transactions in the iterator to the pool, each with its own
670    /// [`TransactionOrigin`], returning a list of results.
671    pub fn add_transactions_with_origins(
672        &self,
673        transactions: impl IntoIterator<
674            Item = (TransactionOrigin, TransactionValidationOutcome<T::Transaction>),
675        >,
676    ) -> Vec<PoolResult<AddedTransactionOutcome>> {
677        let transactions = transactions.into_iter();
678        let mut added_metas = Vec::with_capacity(transactions.size_hint().0);
679
680        // Collect results and metadata while holding the pool write lock
681        let (mut results, added_metas, discarded) = {
682            let mut pool = self.pool.write();
683            let timestamp = Instant::now();
684
685            let results = transactions
686                .map(|(origin, tx)| {
687                    let (result, meta) = self.add_transaction(&mut pool, origin, tx, timestamp);
688
689                    // Only collect metadata for successful insertions
690                    if result.is_ok() &&
691                        let Some(meta) = meta
692                    {
693                        added_metas.push(meta);
694                    }
695
696                    result
697                })
698                .collect::<Vec<_>>();
699
700            // Enforce the pool size limits if at least one transaction was added successfully
701            let discarded = if results.iter().any(Result::is_ok) {
702                let discarded = pool.discard_worst();
703                pool.update_size_metrics();
704                discarded
705            } else {
706                Default::default()
707            };
708
709            (results, added_metas, discarded)
710        };
711
712        for meta in added_metas {
713            self.on_added_transaction(meta);
714        }
715
716        if !discarded.is_empty() {
717            // Delete any blobs associated with discarded blob transactions
718            self.delete_discarded_blobs(discarded.iter());
719            self.with_event_listener(|listener| listener.discarded_many(&discarded));
720
721            // Linear search avoids allocating a hash set for small eviction batches.
722            const MAX_LINEAR_SEARCH_DISCARDS: usize = 4;
723            let discarded_hashes = (discarded.len() > MAX_LINEAR_SEARCH_DISCARDS)
724                .then(|| discarded.iter().map(|tx| *tx.hash()).collect::<HashSet<_>>());
725            let is_discarded = |hash: &TxHash| match &discarded_hashes {
726                Some(hashes) => hashes.contains(hash),
727                None => discarded.iter().any(|tx| tx.hash() == hash),
728            };
729
730            // A newly added transaction may be immediately discarded, so we need to
731            // adjust the result here
732            for res in &mut results {
733                if let Ok(AddedTransactionOutcome { hash, .. }) = res &&
734                    is_discarded(hash)
735                {
736                    *res = Err(PoolError::new(*hash, PoolErrorKind::DiscardedOnInsert))
737                }
738            }
739        };
740
741        results
742    }
743
744    /// Process a transaction that was added to the pool.
745    ///
746    /// Performs blob storage operations and sends all notifications. This should be called
747    /// after the pool write lock has been released to avoid blocking pool operations.
748    fn on_added_transaction(&self, mut meta: AddedTransactionMeta<T::Transaction>) {
749        // Handle blob sidecar storage and notifications for EIP-4844 transactions
750        if let Some(sidecar) = meta.blob_sidecar {
751            let hash = *meta.added.hash();
752            self.on_new_blob_sidecar(&hash, &sidecar);
753            self.insert_blob(hash, sidecar);
754        }
755
756        // Delete replaced blob sidecar if any
757        if let Some(replaced) = meta.added.replaced_blob_transaction() {
758            debug!(target: "txpool", "[{:?}] delete replaced blob sidecar", replaced);
759            self.delete_blob(replaced);
760        }
761
762        // Delete discarded blob sidecars if any, this doesnt do any IO.
763        if let Some(discarded) = meta.added.discarded_transactions() {
764            self.delete_discarded_blobs(discarded.iter());
765        }
766
767        // Notify pending transaction listeners
768        if let Some(pending) = meta.added.as_pending() {
769            self.on_new_pending_transaction(pending);
770        }
771
772        // A parked insertion can promote older transactions when validation observes newer
773        // account state. Notify those promotions independently of the inserted transaction.
774        let promoted = meta.added.take_parked_promoted();
775        if !promoted.is_empty() {
776            self.notify_on_transaction_updates(promoted, Vec::new());
777        }
778
779        // Notify event listeners
780        self.notify_event_listeners(&meta.added);
781
782        // Notify new transaction listeners
783        self.on_new_transaction(meta.added.into_new_transaction_event());
784    }
785
786    /// Notify all listeners about a new pending transaction.
787    ///
788    /// See also [`Self::add_pending_listener`]
789    ///
790    /// CAUTION: This function is only intended to be used manually in order to use this type's
791    /// pending transaction receivers when manually implementing the
792    /// [`TransactionPool`](crate::TransactionPool) trait for a custom pool implementation
793    /// [`TransactionPool::pending_transactions_listener_for`](crate::TransactionPool).
794    pub fn on_new_pending_transaction(&self, pending: &AddedPendingTransaction<T::Transaction>) {
795        let mut needs_cleanup = false;
796
797        {
798            let listeners = self.pending_transaction_listener.read();
799            for listener in listeners.iter() {
800                if !listener.send_all(pending.pending_transactions(listener.kind)) {
801                    needs_cleanup = true;
802                }
803            }
804        }
805
806        // Clean up dead listeners if we detected any closed channels
807        if needs_cleanup {
808            self.pending_transaction_listener
809                .write()
810                .retain(|listener| !listener.sender.is_closed());
811        }
812    }
813
814    /// Notify all listeners about a newly inserted pending transaction.
815    ///
816    /// See also [`Self::add_new_transaction_listener`]
817    ///
818    /// CAUTION: This function is only intended to be used manually in order to use this type's
819    /// transaction receivers when manually implementing the
820    /// [`TransactionPool`](crate::TransactionPool) trait for a custom pool implementation
821    /// [`TransactionPool::new_transactions_listener_for`](crate::TransactionPool).
822    pub fn on_new_transaction(&self, event: NewTransactionEvent<T::Transaction>) {
823        let mut needs_cleanup = false;
824
825        {
826            let listeners = self.transaction_listener.read();
827            for listener in listeners.iter() {
828                if listener.kind.is_propagate_only() && !event.transaction.propagate {
829                    if listener.sender.is_closed() {
830                        needs_cleanup = true;
831                    }
832                    // Skip non-propagate transactions for propagate-only listeners
833                    continue
834                }
835
836                if !listener.send(event.clone()) {
837                    needs_cleanup = true;
838                }
839            }
840        }
841
842        // Clean up dead listeners if we detected any closed channels
843        if needs_cleanup {
844            self.transaction_listener.write().retain(|listener| !listener.sender.is_closed());
845        }
846    }
847
848    /// Notify all listeners about a blob sidecar for a newly inserted blob (eip4844) transaction.
849    fn on_new_blob_sidecar(&self, tx_hash: &TxHash, sidecar: &BlobTransactionSidecarVariant) {
850        let mut sidecar_listeners = self.blob_transaction_sidecar_listener.lock();
851        if sidecar_listeners.is_empty() {
852            return
853        }
854        let sidecar = Arc::new(sidecar.clone());
855        sidecar_listeners.retain_mut(|listener| {
856            let new_blob_event = NewBlobSidecar { tx_hash: *tx_hash, sidecar: sidecar.clone() };
857            match listener.sender.try_send(new_blob_event) {
858                Ok(()) => true,
859                Err(err) => {
860                    if matches!(err, mpsc::error::TrySendError::Full(_)) {
861                        debug!(
862                            target: "txpool",
863                            "[{:?}] failed to send blob sidecar; channel full",
864                            sidecar,
865                        );
866                        true
867                    } else {
868                        false
869                    }
870                }
871            }
872        })
873    }
874
875    /// Notifies transaction listeners about changes once a block was processed.
876    fn notify_on_new_state(&self, outcome: OnNewCanonicalStateOutcome<T::Transaction>) {
877        trace!(target: "txpool", promoted=outcome.promoted.len(), discarded= outcome.discarded.len() ,"notifying listeners on state change");
878
879        // notify about promoted pending transactions - emit hashes
880        let mut needs_pending_cleanup = false;
881        {
882            let listeners = self.pending_transaction_listener.read();
883            for listener in listeners.iter() {
884                if !listener.send_all(outcome.pending_transactions(listener.kind)) {
885                    needs_pending_cleanup = true;
886                }
887            }
888        }
889        if needs_pending_cleanup {
890            self.pending_transaction_listener.write().retain(|l| !l.sender.is_closed());
891        }
892
893        // emit full transactions
894        let mut needs_tx_cleanup = false;
895        {
896            let listeners = self.transaction_listener.read();
897            for listener in listeners.iter() {
898                if !listener.send_all(outcome.full_pending_transactions(listener.kind)) {
899                    needs_tx_cleanup = true;
900                }
901            }
902        }
903        if needs_tx_cleanup {
904            self.transaction_listener.write().retain(|l| !l.sender.is_closed());
905        }
906
907        let OnNewCanonicalStateOutcome { mined, promoted, discarded, block_hash } = outcome;
908
909        // broadcast specific transaction events
910        self.with_event_listener(|listener| {
911            for tx in &mined {
912                listener.mined(tx, block_hash);
913            }
914            for tx in &promoted {
915                listener.pending(tx.hash(), None);
916            }
917            for tx in &discarded {
918                listener.discarded(tx.hash());
919            }
920        })
921    }
922
923    /// Notifies all listeners about the transaction movements.
924    ///
925    /// This will emit events according to the provided changes.
926    ///
927    /// CAUTION: This function is only intended to be used manually in order to use this type's
928    /// [`TransactionEvents`] receivers when manually implementing the
929    /// [`TransactionPool`](crate::TransactionPool) trait for a custom pool implementation
930    /// [`TransactionPool::transaction_event_listener`](crate::TransactionPool).
931    pub fn notify_on_transaction_updates(
932        &self,
933        promoted: Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
934        discarded: Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
935    ) {
936        // Notify about promoted pending transactions (similar to notify_on_new_state)
937        if !promoted.is_empty() {
938            let mut needs_pending_cleanup = false;
939            {
940                let listeners = self.pending_transaction_listener.read();
941                for listener in listeners.iter() {
942                    let promoted_hashes = promoted.iter().filter_map(|tx| {
943                        if listener.kind.is_propagate_only() && !tx.propagate {
944                            None
945                        } else {
946                            Some(*tx.hash())
947                        }
948                    });
949                    if !listener.send_all(promoted_hashes) {
950                        needs_pending_cleanup = true;
951                    }
952                }
953            }
954            if needs_pending_cleanup {
955                self.pending_transaction_listener.write().retain(|l| !l.sender.is_closed());
956            }
957
958            // in this case we should also emit promoted transactions in full
959            let mut needs_tx_cleanup = false;
960            {
961                let listeners = self.transaction_listener.read();
962                for listener in listeners.iter() {
963                    let promoted_txs = promoted.iter().filter_map(|tx| {
964                        if listener.kind.is_propagate_only() && !tx.propagate {
965                            None
966                        } else {
967                            Some(NewTransactionEvent::pending(tx.clone()))
968                        }
969                    });
970                    if !listener.send_all(promoted_txs) {
971                        needs_tx_cleanup = true;
972                    }
973                }
974            }
975            if needs_tx_cleanup {
976                self.transaction_listener.write().retain(|l| !l.sender.is_closed());
977            }
978        }
979
980        self.with_event_listener(|listener| {
981            for tx in &promoted {
982                listener.pending(tx.hash(), None);
983            }
984            for tx in &discarded {
985                listener.discarded(tx.hash());
986            }
987        });
988
989        if !discarded.is_empty() {
990            // This deletes outdated blob txs from the blob store, based on the account's nonce.
991            // This is called during txpool maintenance when the pool drifted.
992            self.delete_discarded_blobs(discarded.iter());
993        }
994    }
995
996    /// Fire events for the newly added transaction if there are any.
997    ///
998    /// See also [`Self::add_transaction_event_listener`].
999    ///
1000    /// CAUTION: This function is only intended to be used manually in order to use this type's
1001    /// [`TransactionEvents`] receivers when manually implementing the
1002    /// [`TransactionPool`](crate::TransactionPool) trait for a custom pool implementation
1003    /// [`TransactionPool::transaction_event_listener`](crate::TransactionPool).
1004    pub fn notify_event_listeners(&self, tx: &AddedTransaction<T::Transaction>) {
1005        self.with_event_listener(|listener| match tx {
1006            AddedTransaction::Pending(tx) => {
1007                let AddedPendingTransaction { transaction, promoted, discarded, replaced } = tx;
1008
1009                listener.pending(transaction.hash(), replaced.clone());
1010                for tx in promoted {
1011                    listener.pending(tx.hash(), None);
1012                }
1013                for tx in discarded {
1014                    listener.discarded(tx.hash());
1015                }
1016            }
1017            AddedTransaction::Parked { transaction, replaced, queued_reason, .. } => {
1018                listener.queued(transaction.hash(), queued_reason.clone());
1019                if let Some(replaced) = replaced {
1020                    listener.replaced(replaced.clone(), *transaction.hash());
1021                }
1022            }
1023        });
1024    }
1025
1026    /// Returns an iterator that yields transactions that are ready to be included in the block.
1027    pub fn best_transactions(&self) -> BestTransactions<T> {
1028        self.get_pool_data().best_transactions()
1029    }
1030
1031    /// Returns an iterator that yields transactions that are ready to be included in the block with
1032    /// the given base fee and optional blob fee attributes.
1033    pub fn best_transactions_with_attributes(
1034        &self,
1035        best_transactions_attributes: BestTransactionsAttributes,
1036    ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
1037    {
1038        self.get_pool_data().best_transactions_with_attributes(best_transactions_attributes)
1039    }
1040
1041    /// Returns only the first `max` transactions in the pending pool.
1042    pub fn pending_transactions_max(
1043        &self,
1044        max: usize,
1045    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1046        self.get_pool_data().pending_transactions_iter().take(max).collect()
1047    }
1048
1049    /// Returns all transactions from the pending sub-pool
1050    pub fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1051        self.get_pool_data().pending_transactions()
1052    }
1053
1054    /// Returns all transactions from parked pools
1055    pub fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1056        self.get_pool_data().queued_transactions()
1057    }
1058
1059    /// Returns all transactions in the pool
1060    pub fn all_transactions(&self) -> AllPoolTransactions<T::Transaction> {
1061        let pool = self.get_pool_data();
1062        AllPoolTransactions {
1063            pending: pool.pending_transactions(),
1064            queued: pool.queued_transactions(),
1065        }
1066    }
1067
1068    /// Returns all transactions of the given sender, collected under a single read guard so that
1069    /// both sides reflect the same pool state.
1070    pub fn all_transactions_by_sender(
1071        &self,
1072        sender: Address,
1073    ) -> AllPoolTransactions<T::Transaction> {
1074        let Some(sender_id) = self.sender_id(&sender) else { return Default::default() };
1075        let pool = self.get_pool_data();
1076        AllPoolTransactions {
1077            pending: pool.pending_txs_by_sender(sender_id),
1078            queued: pool.queued_txs_by_sender(sender_id),
1079        }
1080    }
1081
1082    /// Returns _all_ transactions in the pool
1083    pub fn all_transaction_hashes(&self) -> Vec<TxHash> {
1084        self.get_pool_data().all().transactions_iter().map(|tx| *tx.hash()).collect()
1085    }
1086
1087    /// Removes and returns all matching transactions from the pool.
1088    ///
1089    /// This behaves as if the transactions got discarded (_not_ mined), effectively introducing a
1090    /// nonce gap for the given transactions.
1091    pub fn remove_transactions(
1092        &self,
1093        hashes: Vec<TxHash>,
1094    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1095        if hashes.is_empty() {
1096            return Vec::new()
1097        }
1098        let removed = self.pool.write().remove_transactions(hashes);
1099
1100        self.with_event_listener(|listener| listener.discarded_many(&removed));
1101
1102        removed
1103    }
1104
1105    /// Removes and returns all matching transactions and their dependent transactions from the
1106    /// pool.
1107    pub fn remove_transactions_and_descendants(
1108        &self,
1109        hashes: Vec<TxHash>,
1110    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1111        if hashes.is_empty() {
1112            return Vec::new()
1113        }
1114        let removed = self.pool.write().remove_transactions_and_descendants(hashes);
1115
1116        self.with_event_listener(|listener| {
1117            for tx in &removed {
1118                listener.discarded(tx.hash());
1119            }
1120        });
1121
1122        removed
1123    }
1124
1125    /// Removes and returns all transactions by the specified sender from the pool.
1126    pub fn remove_transactions_by_sender(
1127        &self,
1128        sender: Address,
1129    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1130        let Some(sender_id) = self.sender_id(&sender) else { return Vec::new() };
1131        let removed = self.pool.write().remove_transactions_by_sender(sender_id);
1132
1133        self.with_event_listener(|listener| listener.discarded_many(&removed));
1134
1135        removed
1136    }
1137
1138    /// Prunes and returns all matching transactions from the pool.
1139    ///
1140    /// This removes the transactions as if they were mined: descendant transactions are **not**
1141    /// parked and remain eligible for inclusion.
1142    pub fn prune_transactions(
1143        &self,
1144        hashes: Vec<TxHash>,
1145    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1146        if hashes.is_empty() {
1147            return Vec::new()
1148        }
1149
1150        self.pool.write().prune_transactions(hashes)
1151    }
1152
1153    /// Retains only transactions that are not present in the pool.
1154    pub fn retain_unknown<A>(&self, announcement: &mut A)
1155    where
1156        A: HandleMempoolData,
1157    {
1158        if announcement.is_empty() {
1159            return
1160        }
1161        let pool = self.get_pool_data();
1162        announcement.retain_by_hash(|tx| !pool.contains(tx))
1163    }
1164
1165    /// Retains only transactions that are present in the pool.
1166    pub fn retain_contains<A>(&self, announcement: &mut A)
1167    where
1168        A: HandleMempoolData,
1169    {
1170        if announcement.is_empty() {
1171            return
1172        }
1173        let pool = self.get_pool_data();
1174        announcement.retain_by_hash(|tx| pool.contains(tx))
1175    }
1176
1177    /// Returns the transaction by hash.
1178    pub fn get(&self, tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1179        self.get_pool_data().get(tx_hash)
1180    }
1181
1182    /// Returns all transactions of the address
1183    pub fn get_transactions_by_sender(
1184        &self,
1185        sender: Address,
1186    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1187        let Some(sender_id) = self.sender_id(&sender) else { return Vec::new() };
1188        self.get_pool_data().get_transactions_by_sender(sender_id)
1189    }
1190
1191    /// Returns a pending transaction sent by the given sender with the given nonce.
1192    pub fn get_pending_transaction_by_sender_and_nonce(
1193        &self,
1194        sender: Address,
1195        nonce: u64,
1196    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1197        let sender_id = self.sender_id(&sender)?;
1198        self.get_pool_data().get_pending_transaction_by_sender_and_nonce(sender_id, nonce)
1199    }
1200
1201    /// Returns all queued transactions of the address by sender
1202    pub fn get_queued_transactions_by_sender(
1203        &self,
1204        sender: Address,
1205    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1206        let Some(sender_id) = self.sender_id(&sender) else { return Vec::new() };
1207        self.get_pool_data().queued_txs_by_sender(sender_id)
1208    }
1209
1210    /// Returns all pending transactions filtered by predicate
1211    pub fn pending_transactions_with_predicate(
1212        &self,
1213        predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
1214    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1215        self.get_pool_data().pending_transactions_with_predicate(predicate)
1216    }
1217
1218    /// Returns all pending transactions of the address by sender
1219    pub fn get_pending_transactions_by_sender(
1220        &self,
1221        sender: Address,
1222    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1223        let Some(sender_id) = self.sender_id(&sender) else { return Vec::new() };
1224        self.get_pending_transactions_by_sender_id(sender_id)
1225    }
1226
1227    /// Returns all pending transactions for a resolved sender ID.
1228    pub fn get_pending_transactions_by_sender_id(
1229        &self,
1230        sender_id: SenderId,
1231    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1232        self.get_pool_data().pending_txs_by_sender(sender_id)
1233    }
1234
1235    /// Returns the highest transaction of the address
1236    pub fn get_highest_transaction_by_sender(
1237        &self,
1238        sender: Address,
1239    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1240        let sender_id = self.sender_id(&sender)?;
1241        self.get_highest_transaction_by_sender_id(sender_id)
1242    }
1243
1244    /// Returns the highest transaction for a resolved sender ID.
1245    pub fn get_highest_transaction_by_sender_id(
1246        &self,
1247        sender_id: SenderId,
1248    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1249        self.get_pool_data().get_highest_transaction_by_sender(sender_id)
1250    }
1251
1252    /// Returns the transaction with the highest nonce that is executable given the on chain nonce.
1253    pub fn get_highest_consecutive_transaction_by_sender(
1254        &self,
1255        sender: Address,
1256        on_chain_nonce: u64,
1257    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1258        let sender_id = self.sender_id(&sender)?;
1259        self.get_highest_consecutive_transaction_by_sender_id(sender_id, on_chain_nonce)
1260    }
1261
1262    /// Returns the highest consecutive transaction for a resolved sender ID and on-chain nonce.
1263    pub fn get_highest_consecutive_transaction_by_sender_id(
1264        &self,
1265        sender_id: SenderId,
1266        on_chain_nonce: u64,
1267    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1268        self.get_pool_data().get_highest_consecutive_transaction_by_sender(
1269            sender_id.into_transaction_id(on_chain_nonce),
1270        )
1271    }
1272
1273    /// Returns the transaction given a [`TransactionId`]
1274    pub fn get_transaction_by_transaction_id(
1275        &self,
1276        transaction_id: &TransactionId,
1277    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1278        self.get_pool_data().all().get(transaction_id).map(|tx| tx.transaction.clone())
1279    }
1280
1281    /// Returns all transactions that where submitted with the given [`TransactionOrigin`]
1282    pub fn get_transactions_by_origin(
1283        &self,
1284        origin: TransactionOrigin,
1285    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1286        self.get_pool_data()
1287            .all()
1288            .transactions_iter()
1289            .filter(|tx| tx.origin == origin)
1290            .cloned()
1291            .collect()
1292    }
1293
1294    /// Returns all pending transactions filtered by [`TransactionOrigin`]
1295    pub fn get_pending_transactions_by_origin(
1296        &self,
1297        origin: TransactionOrigin,
1298    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1299        self.get_pool_data().pending_transactions_iter().filter(|tx| tx.origin == origin).collect()
1300    }
1301
1302    /// Returns all the transactions belonging to the hashes.
1303    ///
1304    /// If no transaction exists, it is skipped.
1305    pub fn get_all(&self, txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1306        if txs.is_empty() {
1307            return Vec::new()
1308        }
1309        self.get_pool_data().get_all(txs).collect()
1310    }
1311
1312    /// Returns all the transactions belonging to the hashes that are propagatable.
1313    ///
1314    /// If no transaction exists, it is skipped.
1315    fn get_all_propagatable(
1316        &self,
1317        txs: &[TxHash],
1318    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1319        if txs.is_empty() {
1320            return Vec::new()
1321        }
1322        let pool = self.get_pool_data();
1323        txs.iter().filter_map(|tx| pool.get(tx).filter(|tx| tx.propagate)).collect()
1324    }
1325
1326    /// Notify about propagated transactions.
1327    pub fn on_propagated(&self, txs: PropagatedTransactions) {
1328        if txs.is_empty() {
1329            return
1330        }
1331        self.with_event_listener(|listener| {
1332            txs.into_iter().for_each(|(hash, peers)| listener.propagated(&hash, peers));
1333        });
1334    }
1335
1336    /// Number of transactions in the entire pool
1337    pub fn len(&self) -> usize {
1338        self.get_pool_data().len()
1339    }
1340
1341    /// Whether the pool is empty
1342    pub fn is_empty(&self) -> bool {
1343        self.get_pool_data().is_empty()
1344    }
1345
1346    /// Returns whether or not the pool is over its configured size and transaction count limits.
1347    pub fn is_exceeded(&self) -> bool {
1348        self.pool.read().is_exceeded()
1349    }
1350
1351    /// Inserts a blob transaction into the blob store
1352    fn insert_blob(&self, hash: TxHash, blob: PooledBlobSidecar) {
1353        debug!(target: "txpool", "[{:?}] storing blob sidecar", hash);
1354        if let Err(err) = self.blob_store.insert(hash, blob) {
1355            warn!(target: "txpool", %err, "[{:?}] failed to insert blob", hash);
1356            self.blob_store_metrics.blobstore_failed_inserts.increment(1);
1357        }
1358        self.update_blob_store_metrics();
1359    }
1360
1361    /// Delete a blob from the blob store
1362    pub fn delete_blob(&self, blob: TxHash) {
1363        let _ = self.blob_store.delete(blob);
1364    }
1365
1366    /// Delete all blobs from the blob store
1367    pub fn delete_blobs(&self, txs: Vec<TxHash>) {
1368        let _ = self.blob_store.delete_all(txs);
1369    }
1370
1371    /// Cleans up the blob store
1372    pub fn cleanup_blobs(&self) {
1373        let stat = self.blob_store.cleanup();
1374        self.blob_store_metrics.blobstore_failed_deletes.increment(stat.delete_failed as u64);
1375        self.update_blob_store_metrics();
1376    }
1377
1378    fn update_blob_store_metrics(&self) {
1379        if let Some(data_size) = self.blob_store.data_size_hint() {
1380            self.blob_store_metrics.blobstore_byte_size.set(data_size as f64);
1381        }
1382        self.blob_store_metrics.blobstore_entries.set(self.blob_store.blobs_len() as f64);
1383    }
1384
1385    /// Deletes all blob transactions that were discarded.
1386    fn delete_discarded_blobs<'a>(
1387        &'a self,
1388        transactions: impl IntoIterator<Item = &'a Arc<ValidPoolTransaction<T::Transaction>>>,
1389    ) {
1390        let blob_txs = transactions
1391            .into_iter()
1392            .filter(|tx| tx.transaction.is_eip4844())
1393            .map(|tx| *tx.hash())
1394            .collect();
1395        self.delete_blobs(blob_txs);
1396    }
1397}
1398
1399impl<V, T: TransactionOrdering, S> fmt::Debug for PoolInner<V, T, S> {
1400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1401        f.debug_struct("PoolInner").field("config", &self.config).finish_non_exhaustive()
1402    }
1403}
1404
1405/// Metadata for a transaction that was added to the pool.
1406///
1407/// This holds all the data needed to complete post-insertion operations (notifications,
1408/// blob storage).
1409#[derive(Debug)]
1410struct AddedTransactionMeta<T: PoolTransaction> {
1411    /// The transaction that was added to the pool
1412    added: AddedTransaction<T>,
1413    /// Optional blob sidecar for EIP-4844 transactions
1414    blob_sidecar: Option<PooledBlobSidecar>,
1415}
1416
1417/// Tracks an added transaction and all graph changes caused by adding it.
1418#[derive(Debug, Clone)]
1419pub struct AddedPendingTransaction<T: PoolTransaction> {
1420    /// Inserted transaction.
1421    pub transaction: Arc<ValidPoolTransaction<T>>,
1422    /// Replaced transaction.
1423    pub replaced: Option<Arc<ValidPoolTransaction<T>>>,
1424    /// transactions promoted to the pending queue
1425    pub promoted: Vec<Arc<ValidPoolTransaction<T>>>,
1426    /// transactions that failed and became discarded
1427    pub discarded: Vec<Arc<ValidPoolTransaction<T>>>,
1428}
1429
1430impl<T: PoolTransaction> AddedPendingTransaction<T> {
1431    /// Returns all transactions that were promoted to the pending pool and adhere to the given
1432    /// [`TransactionListenerKind`].
1433    ///
1434    /// If the kind is [`TransactionListenerKind::PropagateOnly`], then only transactions that
1435    /// are allowed to be propagated are returned.
1436    pub(crate) fn pending_transactions(
1437        &self,
1438        kind: TransactionListenerKind,
1439    ) -> impl Iterator<Item = B256> + '_ {
1440        let iter = std::iter::once(&self.transaction).chain(self.promoted.iter());
1441        PendingTransactionIter { kind, iter }
1442    }
1443}
1444
1445pub(crate) struct PendingTransactionIter<Iter> {
1446    kind: TransactionListenerKind,
1447    iter: Iter,
1448}
1449
1450impl<'a, Iter, T> Iterator for PendingTransactionIter<Iter>
1451where
1452    Iter: Iterator<Item = &'a Arc<ValidPoolTransaction<T>>>,
1453    T: PoolTransaction + 'a,
1454{
1455    type Item = B256;
1456
1457    fn next(&mut self) -> Option<Self::Item> {
1458        loop {
1459            let next = self.iter.next()?;
1460            if self.kind.is_propagate_only() && !next.propagate {
1461                continue
1462            }
1463            return Some(*next.hash())
1464        }
1465    }
1466}
1467
1468/// An iterator over full pending transactions
1469pub(crate) struct FullPendingTransactionIter<Iter> {
1470    kind: TransactionListenerKind,
1471    iter: Iter,
1472}
1473
1474impl<'a, Iter, T> Iterator for FullPendingTransactionIter<Iter>
1475where
1476    Iter: Iterator<Item = &'a Arc<ValidPoolTransaction<T>>>,
1477    T: PoolTransaction + 'a,
1478{
1479    type Item = NewTransactionEvent<T>;
1480
1481    fn next(&mut self) -> Option<Self::Item> {
1482        loop {
1483            let next = self.iter.next()?;
1484            if self.kind.is_propagate_only() && !next.propagate {
1485                continue
1486            }
1487            return Some(NewTransactionEvent {
1488                subpool: SubPool::Pending,
1489                transaction: next.clone(),
1490            })
1491        }
1492    }
1493}
1494
1495/// Represents a transaction that was added into the pool and its state
1496#[derive(Debug, Clone)]
1497pub enum AddedTransaction<T: PoolTransaction> {
1498    /// Transaction was successfully added and moved to the pending pool.
1499    Pending(AddedPendingTransaction<T>),
1500    /// Transaction was successfully added but not yet ready for processing and moved to a
1501    /// parked pool instead.
1502    Parked {
1503        /// Inserted transaction.
1504        transaction: Arc<ValidPoolTransaction<T>>,
1505        /// Replaced transaction.
1506        replaced: Option<Arc<ValidPoolTransaction<T>>>,
1507        /// The subpool it was moved to.
1508        subpool: SubPool,
1509        /// The specific reason why the transaction is queued (if applicable).
1510        queued_reason: Option<QueuedReason>,
1511        /// Existing transactions promoted to pending by this insertion.
1512        ///
1513        /// Validation can observe newer account state before pool maintenance applies it.
1514        /// Insertion rechecks the sender's transactions against that state, so lower-nonce
1515        /// transactions can become pending while the inserted transaction remains parked.
1516        promoted: Vec<Arc<ValidPoolTransaction<T>>>,
1517    },
1518}
1519
1520impl<T: PoolTransaction> AddedTransaction<T> {
1521    /// Returns whether the transaction has been added to the pending pool.
1522    pub const fn as_pending(&self) -> Option<&AddedPendingTransaction<T>> {
1523        match self {
1524            Self::Pending(tx) => Some(tx),
1525            _ => None,
1526        }
1527    }
1528
1529    /// Returns the replaced transaction if there was one
1530    pub const fn replaced(&self) -> Option<&Arc<ValidPoolTransaction<T>>> {
1531        match self {
1532            Self::Pending(tx) => tx.replaced.as_ref(),
1533            Self::Parked { replaced, .. } => replaced.as_ref(),
1534        }
1535    }
1536
1537    /// Takes promotions caused by a parked insertion for separate notification.
1538    ///
1539    /// Call before notifying listeners about the inserted transaction. This drains only the
1540    /// parked promotions; subsequent parked event conversion handles the inserted transaction.
1541    pub(crate) fn take_parked_promoted(&mut self) -> Vec<Arc<ValidPoolTransaction<T>>> {
1542        match self {
1543            Self::Parked { promoted, .. } => std::mem::take(promoted),
1544            Self::Pending(_) => Vec::new(),
1545        }
1546    }
1547
1548    /// Returns the discarded transactions if there were any
1549    pub(crate) fn discarded_transactions(&self) -> Option<&[Arc<ValidPoolTransaction<T>>]> {
1550        match self {
1551            Self::Pending(tx) => Some(&tx.discarded),
1552            Self::Parked { .. } => None,
1553        }
1554    }
1555
1556    /// Returns the hash of the replaced transaction if it is a blob transaction.
1557    pub(crate) fn replaced_blob_transaction(&self) -> Option<B256> {
1558        self.replaced().filter(|tx| tx.transaction.is_eip4844()).map(|tx| *tx.transaction.hash())
1559    }
1560
1561    /// Returns the hash of the transaction
1562    pub fn hash(&self) -> &TxHash {
1563        match self {
1564            Self::Pending(tx) => tx.transaction.hash(),
1565            Self::Parked { transaction, .. } => transaction.hash(),
1566        }
1567    }
1568
1569    /// Converts this type into the event type for listeners
1570    pub fn into_new_transaction_event(self) -> NewTransactionEvent<T> {
1571        match self {
1572            Self::Pending(tx) => {
1573                NewTransactionEvent { subpool: SubPool::Pending, transaction: tx.transaction }
1574            }
1575            Self::Parked { transaction, subpool, .. } => {
1576                NewTransactionEvent { transaction, subpool }
1577            }
1578        }
1579    }
1580
1581    /// Returns the subpool this transaction was added to
1582    pub(crate) const fn subpool(&self) -> SubPool {
1583        match self {
1584            Self::Pending(_) => SubPool::Pending,
1585            Self::Parked { subpool, .. } => *subpool,
1586        }
1587    }
1588
1589    /// Returns the [`TransactionId`] of the added transaction
1590    #[cfg(test)]
1591    pub(crate) fn id(&self) -> &TransactionId {
1592        match self {
1593            Self::Pending(added) => added.transaction.id(),
1594            Self::Parked { transaction, .. } => transaction.id(),
1595        }
1596    }
1597
1598    /// Returns the queued reason if the transaction is parked with a queued reason.
1599    pub const fn queued_reason(&self) -> Option<&QueuedReason> {
1600        match self {
1601            Self::Pending(_) => None,
1602            Self::Parked { queued_reason, .. } => queued_reason.as_ref(),
1603        }
1604    }
1605
1606    /// Returns the transaction state based on the subpool and queued reason.
1607    pub fn transaction_state(&self) -> AddedTransactionState {
1608        match self.subpool() {
1609            SubPool::Pending => AddedTransactionState::Pending,
1610            _ => {
1611                // For non-pending transactions, use the queued reason directly from the
1612                // AddedTransaction
1613                if let Some(reason) = self.queued_reason() {
1614                    AddedTransactionState::Queued(reason.clone())
1615                } else {
1616                    // Fallback - this shouldn't happen with the new implementation
1617                    AddedTransactionState::Queued(QueuedReason::NonceGap)
1618                }
1619            }
1620        }
1621    }
1622}
1623
1624/// The specific reason why a transaction is queued (not ready for execution)
1625#[derive(Debug, Clone, PartialEq, Eq)]
1626pub enum QueuedReason {
1627    /// Transaction has a nonce gap - missing prior transactions
1628    NonceGap,
1629    /// Transaction has parked ancestors - waiting for other transactions to be mined
1630    ParkedAncestors,
1631    /// Sender has insufficient balance to cover the transaction cost
1632    InsufficientBalance,
1633    /// Transaction exceeds the block gas limit
1634    TooMuchGas,
1635    /// Transaction doesn't meet the base fee requirement
1636    InsufficientBaseFee,
1637    /// Transaction doesn't meet the blob fee requirement (EIP-4844)
1638    InsufficientBlobFee,
1639}
1640
1641/// The state of a transaction when is was added to the pool
1642#[derive(Debug, Clone, PartialEq, Eq)]
1643pub enum AddedTransactionState {
1644    /// Ready for execution
1645    Pending,
1646    /// Not ready for execution due to a specific condition
1647    Queued(QueuedReason),
1648}
1649
1650impl AddedTransactionState {
1651    /// Returns whether the transaction was submitted as queued.
1652    pub const fn is_queued(&self) -> bool {
1653        matches!(self, Self::Queued(_))
1654    }
1655
1656    /// Returns whether the transaction was submitted as pending.
1657    pub const fn is_pending(&self) -> bool {
1658        matches!(self, Self::Pending)
1659    }
1660
1661    /// Returns the specific queued reason if the transaction is queued.
1662    pub const fn queued_reason(&self) -> Option<&QueuedReason> {
1663        match self {
1664            Self::Queued(reason) => Some(reason),
1665            Self::Pending => None,
1666        }
1667    }
1668}
1669
1670/// The outcome of a successful transaction addition
1671#[derive(Debug, Clone, PartialEq, Eq)]
1672pub struct AddedTransactionOutcome {
1673    /// The hash of the transaction
1674    pub hash: TxHash,
1675    /// The state of the transaction
1676    pub state: AddedTransactionState,
1677}
1678
1679impl AddedTransactionOutcome {
1680    /// Returns whether the transaction was submitted as queued.
1681    pub const fn is_queued(&self) -> bool {
1682        self.state.is_queued()
1683    }
1684
1685    /// Returns whether the transaction was submitted as pending.
1686    pub const fn is_pending(&self) -> bool {
1687        self.state.is_pending()
1688    }
1689}
1690
1691/// Contains all state changes after a [`CanonicalStateUpdate`] was processed
1692#[derive(Debug)]
1693pub(crate) struct OnNewCanonicalStateOutcome<T: PoolTransaction> {
1694    /// Hash of the block.
1695    pub(crate) block_hash: B256,
1696    /// All mined transactions.
1697    pub(crate) mined: Vec<TxHash>,
1698    /// Transactions promoted to the pending pool.
1699    pub(crate) promoted: Vec<Arc<ValidPoolTransaction<T>>>,
1700    /// transaction that were discarded during the update
1701    pub(crate) discarded: Vec<Arc<ValidPoolTransaction<T>>>,
1702}
1703
1704impl<T: PoolTransaction> OnNewCanonicalStateOutcome<T> {
1705    /// Returns all transactions that were promoted to the pending pool and adhere to the given
1706    /// [`TransactionListenerKind`].
1707    ///
1708    /// If the kind is [`TransactionListenerKind::PropagateOnly`], then only transactions that
1709    /// are allowed to be propagated are returned.
1710    pub(crate) fn pending_transactions(
1711        &self,
1712        kind: TransactionListenerKind,
1713    ) -> impl Iterator<Item = B256> + '_ {
1714        let iter = self.promoted.iter();
1715        PendingTransactionIter { kind, iter }
1716    }
1717
1718    /// Returns all FULL transactions that were promoted to the pending pool and adhere to the given
1719    /// [`TransactionListenerKind`].
1720    ///
1721    /// If the kind is [`TransactionListenerKind::PropagateOnly`], then only transactions that
1722    /// are allowed to be propagated are returned.
1723    pub(crate) fn full_pending_transactions(
1724        &self,
1725        kind: TransactionListenerKind,
1726    ) -> impl Iterator<Item = NewTransactionEvent<T>> + '_ {
1727        let iter = self.promoted.iter();
1728        FullPendingTransactionIter { kind, iter }
1729    }
1730}
1731
1732#[cfg(test)]
1733mod tests {
1734    use crate::{
1735        blobstore::{BlobStore, InMemoryBlobStore, PooledBlobSidecar},
1736        identifier::SenderId,
1737        test_utils::{testing_pool, MockTransaction, TestPool, TestPoolBuilder},
1738        validate::ValidTransaction,
1739        BlockInfo, FullTransactionEvent, PoolConfig, SubPool, SubPoolLimit,
1740        TransactionListenerKind, TransactionOrigin, TransactionPool, TransactionPoolExt,
1741        TransactionValidationOutcome, ValidPoolTransaction, U256,
1742    };
1743    use alloy_consensus::Transaction;
1744    use alloy_eips::{eip4844::BlobTransactionSidecar, eip7594::BlobTransactionSidecarVariant};
1745    use alloy_primitives::{Address, B256};
1746    use futures_util::{FutureExt, StreamExt};
1747    use std::{fs, path::PathBuf, sync::Arc};
1748    use tokio::sync::mpsc::error::TryRecvError;
1749
1750    /// Inserts a transaction with an explicit validator snapshot, without running maintenance.
1751    fn insert_with_state_nonce(pool: &TestPool, nonce: u64, state_nonce: u64, propagate: bool) {
1752        let transaction = MockTransaction::eip1559()
1753            .with_sender(Address::with_last_byte(1))
1754            .with_nonce(nonce)
1755            .with_hash(B256::from([nonce as u8; 32]));
1756        pool.pool
1757            .add_transactions(
1758                TransactionOrigin::External,
1759                [TransactionValidationOutcome::Valid {
1760                    balance: U256::MAX,
1761                    state_nonce,
1762                    bytecode_hash: None,
1763                    transaction: ValidTransaction::Valid(transaction),
1764                    propagate,
1765                    authorities: None,
1766                }],
1767            )
1768            .pop()
1769            .unwrap()
1770            .unwrap();
1771    }
1772
1773    fn transaction_nonces(txs: &[Arc<ValidPoolTransaction<MockTransaction>>]) -> Vec<u64> {
1774        let mut nonces: Vec<_> = txs.iter().map(|tx| tx.transaction.nonce()).collect();
1775        nonces.sort_unstable();
1776        nonces
1777    }
1778
1779    #[tokio::test]
1780    async fn all_transactions_by_sender_across_reclassification() {
1781        let pool = testing_pool();
1782        let sender = Address::with_last_byte(1);
1783        // nonces 0 and 1 are pending, the gapped nonce 9 is queued
1784        for nonce in [0, 1, 9] {
1785            let tx =
1786                MockTransaction::legacy().with_sender(sender).with_nonce(nonce).with_gas_price(100);
1787            pool.add_transaction(TransactionOrigin::External, tx).await.unwrap();
1788        }
1789        let nonces = |txs: &[Arc<ValidPoolTransaction<MockTransaction>>]| {
1790            let mut nonces: Vec<_> = txs.iter().map(|tx| tx.transaction.nonce()).collect();
1791            nonces.sort_unstable();
1792            nonces
1793        };
1794
1795        let txs = pool.all_transactions_by_sender(sender);
1796        assert_eq!(nonces(&txs.pending), [0, 1]);
1797        assert_eq!(nonces(&txs.queued), [9]);
1798
1799        let sender_id = pool.inner().sender_id(&sender).unwrap();
1800        assert_eq!(nonces(&pool.inner().get_pending_transactions_by_sender_id(sender_id)), [0, 1]);
1801        assert_eq!(
1802            pool.inner().get_highest_transaction_by_sender_id(sender_id).unwrap().nonce(),
1803            9
1804        );
1805        assert_eq!(
1806            pool.inner()
1807                .get_highest_consecutive_transaction_by_sender_id(sender_id, 0)
1808                .unwrap()
1809                .nonce(),
1810            1
1811        );
1812
1813        // a higher base fee reclassifies the pending transactions as queued; the snapshot must
1814        // report each of them on exactly one side
1815        pool.set_block_info(BlockInfo {
1816            pending_basefee: 200,
1817            block_gas_limit: 30_000_000,
1818            ..Default::default()
1819        });
1820        let txs = pool.all_transactions_by_sender(sender);
1821        assert!(txs.pending.is_empty());
1822        assert_eq!(nonces(&txs.queued), [0, 1, 9]);
1823        assert_eq!(nonces(&pool.get_transactions_by_sender(sender)), [0, 1, 9]);
1824    }
1825
1826    #[test]
1827    fn queued_insertion_notifies_older_promotions() {
1828        let test_pool = testing_pool();
1829        let pool = &test_pool.pool;
1830        let mut network = pool.add_pending_listener(TransactionListenerKind::PropagateOnly);
1831        let mut all = pool.add_pending_listener(TransactionListenerKind::All);
1832        // Missing nonce 0 keeps both transactions queued.
1833        insert_with_state_nonce(&test_pool, 1, 0, true);
1834        insert_with_state_nonce(&test_pool, 2, 0, true);
1835        let txs = test_pool.all_transactions_by_sender(Address::with_last_byte(1));
1836        assert!(txs.pending.is_empty());
1837        assert_eq!(transaction_nonces(&txs.queued), [1, 2]);
1838        assert_eq!(network.try_recv(), Err(TryRecvError::Empty));
1839        assert_eq!(all.try_recv(), Err(TryRecvError::Empty));
1840
1841        let mut full_network =
1842            pool.add_new_transaction_listener(TransactionListenerKind::PropagateOnly);
1843        let mut full_all = pool.add_new_transaction_listener(TransactionListenerKind::All);
1844        let mut events = pool.add_all_transactions_event_listener();
1845
1846        // A newer validation result says nonce 0 is mined. Nonces 1 and 2 become
1847        // pending, but inserting 4 leaves a gap at 3.
1848        insert_with_state_nonce(&test_pool, 4, 1, true);
1849        let txs = test_pool.all_transactions_by_sender(Address::with_last_byte(1));
1850        assert_eq!(transaction_nonces(&txs.pending), [1, 2]);
1851        assert_eq!(transaction_nonces(&txs.queued), [4]);
1852
1853        let expected = vec![B256::from([1; 32]), B256::from([2; 32])];
1854        for (kind, listener) in [("network", &mut network), ("all", &mut all)] {
1855            let mut received = Vec::new();
1856            while let Ok(hash) = listener.try_recv() {
1857                received.push(hash);
1858            }
1859            received.sort_unstable();
1860            assert_eq!(received, expected, "{kind} pending notifications");
1861        }
1862
1863        // Full-transaction subscribers get each promotion once and the inserted
1864        // transaction once, still marked queued. Propagation filtering applies here too.
1865        for (kind, listener) in [("network", &mut full_network), ("all", &mut full_all)] {
1866            let mut received = Vec::new();
1867            while let Ok(event) = listener.try_recv() {
1868                received.push((*event.transaction.hash(), event.subpool));
1869            }
1870            received.sort_unstable_by_key(|(hash, _)| *hash);
1871            let expected = vec![
1872                (B256::from([1; 32]), SubPool::Pending),
1873                (B256::from([2; 32]), SubPool::Pending),
1874                (B256::from([4; 32]), SubPool::Queued),
1875            ];
1876            assert_eq!(received, expected, "{kind} full transaction notifications");
1877        }
1878
1879        let mut pending_events = Vec::new();
1880        let mut queued_events = Vec::new();
1881        while let Some(Some(event)) = events.next().now_or_never() {
1882            match event {
1883                FullTransactionEvent::Pending(hash) => pending_events.push(hash),
1884                FullTransactionEvent::Queued(hash, _) => queued_events.push(hash),
1885                other => panic!("unexpected event: {other:?}"),
1886            }
1887        }
1888        pending_events.sort_unstable();
1889        assert_eq!(pending_events, expected);
1890        assert_eq!(queued_events, vec![B256::from([4; 32])]);
1891
1892        // A later insertion observing the same state must not notify those promotions again.
1893        insert_with_state_nonce(&test_pool, 5, 1, true);
1894        assert_eq!(network.try_recv(), Err(TryRecvError::Empty));
1895        assert_eq!(all.try_recv(), Err(TryRecvError::Empty));
1896        for listener in [&mut full_network, &mut full_all] {
1897            let event = listener.try_recv().unwrap();
1898            assert_eq!(*event.transaction.hash(), B256::from([5; 32]));
1899            assert_eq!(event.subpool, SubPool::Queued);
1900            assert!(matches!(listener.try_recv(), Err(TryRecvError::Empty)));
1901        }
1902        assert!(matches!(
1903            events.next().now_or_never(),
1904            Some(Some(FullTransactionEvent::Queued(hash, _))) if hash == B256::from([5; 32])
1905        ));
1906        assert!(events.next().now_or_never().is_none());
1907    }
1908
1909    #[test]
1910    fn queued_insertion_promotions_respect_propagation_filter() {
1911        let test_pool = testing_pool();
1912        let pool = &test_pool.pool;
1913        insert_with_state_nonce(&test_pool, 1, 0, false);
1914        insert_with_state_nonce(&test_pool, 2, 0, true);
1915
1916        let mut network = pool.add_pending_listener(TransactionListenerKind::PropagateOnly);
1917        let mut all = pool.add_pending_listener(TransactionListenerKind::All);
1918        let mut full_network =
1919            pool.add_new_transaction_listener(TransactionListenerKind::PropagateOnly);
1920        let mut full_all = pool.add_new_transaction_listener(TransactionListenerKind::All);
1921        insert_with_state_nonce(&test_pool, 4, 1, true);
1922
1923        assert_eq!(network.try_recv().unwrap(), B256::from([2; 32]));
1924        assert_eq!(network.try_recv(), Err(TryRecvError::Empty));
1925        let mut received = Vec::new();
1926        while let Ok(hash) = all.try_recv() {
1927            received.push(hash);
1928        }
1929        received.sort_unstable();
1930        assert_eq!(received, [B256::from([1; 32]), B256::from([2; 32])]);
1931
1932        for (listener, expected) in [
1933            (
1934                &mut full_network,
1935                vec![
1936                    (B256::from([2; 32]), SubPool::Pending),
1937                    (B256::from([4; 32]), SubPool::Queued),
1938                ],
1939            ),
1940            (
1941                &mut full_all,
1942                vec![
1943                    (B256::from([1; 32]), SubPool::Pending),
1944                    (B256::from([2; 32]), SubPool::Pending),
1945                    (B256::from([4; 32]), SubPool::Queued),
1946                ],
1947            ),
1948        ] {
1949            let mut received = Vec::new();
1950            while let Ok(event) = listener.try_recv() {
1951                received.push((*event.transaction.hash(), event.subpool));
1952            }
1953            received.sort_unstable_by_key(|(hash, _)| *hash);
1954            assert_eq!(received, expected);
1955        }
1956    }
1957
1958    #[test]
1959    fn pending_insertion_notifies_older_promotions() {
1960        let test_pool = testing_pool();
1961        let pool = &test_pool.pool;
1962        insert_with_state_nonce(&test_pool, 1, 0, true);
1963        insert_with_state_nonce(&test_pool, 2, 0, true);
1964        let mut network = pool.add_pending_listener(TransactionListenerKind::PropagateOnly);
1965        let mut all = pool.add_pending_listener(TransactionListenerKind::All);
1966
1967        // Filling the gap makes both the older transactions and the insertion pending.
1968        insert_with_state_nonce(&test_pool, 3, 1, true);
1969        let txs = test_pool.all_transactions_by_sender(Address::with_last_byte(1));
1970        assert_eq!(transaction_nonces(&txs.pending), [1, 2, 3]);
1971        assert!(txs.queued.is_empty());
1972        for listener in [&mut network, &mut all] {
1973            let mut received = Vec::new();
1974            while let Ok(hash) = listener.try_recv() {
1975                received.push(hash);
1976            }
1977            received.sort_unstable();
1978            assert_eq!(received, [B256::from([1; 32]), B256::from([2; 32]), B256::from([3; 32])]);
1979        }
1980    }
1981
1982    #[test]
1983    fn test_discard_blobs_on_blob_tx_eviction() {
1984        let blobs = {
1985            // Read the contents of the JSON file into a string.
1986            let json_content = fs::read_to_string(
1987                PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test_data/blob1.json"),
1988            )
1989            .expect("Failed to read the blob data file");
1990
1991            // Parse the JSON contents into a serde_json::Value.
1992            let json_value: serde_json::Value =
1993                serde_json::from_str(&json_content).expect("Failed to deserialize JSON");
1994
1995            // Extract blob data from JSON and convert it to Blob.
1996            vec![
1997                // Extract the "data" field from the JSON and parse it as a string.
1998                json_value
1999                    .get("data")
2000                    .unwrap()
2001                    .as_str()
2002                    .expect("Data is not a valid string")
2003                    .to_string(),
2004            ]
2005        };
2006
2007        // Generate a BlobTransactionSidecar from the blobs.
2008        let sidecar = BlobTransactionSidecarVariant::Eip4844(
2009            BlobTransactionSidecar::try_from_blobs_hex(blobs).unwrap(),
2010        );
2011
2012        // Define the maximum limit for blobs in the sub-pool.
2013        let blob_limit = SubPoolLimit::new(1000, usize::MAX);
2014
2015        // Create a test pool with default configuration and the specified blob limit.
2016        let test_pool = &TestPoolBuilder::default()
2017            .with_config(PoolConfig { blob_limit, ..Default::default() })
2018            .pool;
2019
2020        // Set the block info for the pool, including a pending blob fee.
2021        test_pool
2022            .set_block_info(BlockInfo { pending_blob_fee: Some(10_000_000), ..Default::default() });
2023
2024        // Create an in-memory blob store.
2025        let blob_store = InMemoryBlobStore::default();
2026
2027        // Loop to add transactions to the pool and test blob eviction.
2028        for n in 0..blob_limit.max_txs + 10 {
2029            // Create a mock transaction with the generated blob sidecar.
2030            let mut tx = MockTransaction::eip4844_with_sidecar(sidecar.clone());
2031
2032            // Set non zero size
2033            tx.set_size(1844674407370951);
2034
2035            // Insert the sidecar into the blob store if the current index is within the blob limit.
2036            if n < blob_limit.max_txs {
2037                blob_store.insert(*tx.get_hash(), sidecar.clone().into()).unwrap();
2038            }
2039
2040            // Add the transaction to the pool with external origin and valid outcome.
2041            test_pool.add_transactions(
2042                TransactionOrigin::External,
2043                [TransactionValidationOutcome::Valid {
2044                    balance: U256::from(1_000),
2045                    state_nonce: 0,
2046                    bytecode_hash: None,
2047                    transaction: ValidTransaction::ValidWithSidecar {
2048                        transaction: tx,
2049                        sidecar: PooledBlobSidecar::from(sidecar.clone()),
2050                    },
2051                    propagate: true,
2052                    authorities: None,
2053                }],
2054            );
2055        }
2056
2057        // Assert that the size of the pool's blob component is equal to the maximum blob limit.
2058        assert_eq!(test_pool.size().blob, blob_limit.max_txs);
2059
2060        // Assert that the size of the pool's blob_size component matches the expected value.
2061        assert_eq!(test_pool.size().blob_size, 1844674407370951000);
2062
2063        // Assert that the pool's blob store matches the expected blob store.
2064        assert_eq!(*test_pool.blob_store(), blob_store);
2065    }
2066
2067    #[test]
2068    fn test_auths_stored_in_identifiers() {
2069        // Create a test pool with default configuration.
2070        let test_pool = &TestPoolBuilder::default().with_config(Default::default()).pool;
2071
2072        let auth = Address::new([1; 20]);
2073        let tx = MockTransaction::eip7702();
2074
2075        test_pool.add_transactions(
2076            TransactionOrigin::Local,
2077            [TransactionValidationOutcome::Valid {
2078                balance: U256::from(1_000),
2079                state_nonce: 0,
2080                bytecode_hash: None,
2081                transaction: ValidTransaction::Valid(tx),
2082                propagate: true,
2083                authorities: Some(vec![auth]),
2084            }],
2085        );
2086
2087        let identifiers = test_pool.identifiers.read();
2088        assert_eq!(identifiers.sender_id(&auth), Some(SenderId::from(1)));
2089    }
2090
2091    #[test]
2092    fn sender_queries_do_not_allocate_ids_for_unknown_addresses() {
2093        let test_pool = &TestPoolBuilder::default().with_config(Default::default()).pool;
2094        let sender = Address::new([9; 20]);
2095
2096        assert_eq!(test_pool.sender_id(&sender), None);
2097        assert!(test_pool.get_transactions_by_sender(sender).is_empty());
2098        assert!(test_pool.get_pending_transaction_by_sender_and_nonce(sender, 0).is_none());
2099        assert!(test_pool.get_queued_transactions_by_sender(sender).is_empty());
2100        assert!(test_pool.get_pending_transactions_by_sender(sender).is_empty());
2101        assert!(test_pool.get_highest_transaction_by_sender(sender).is_none());
2102        assert!(test_pool.get_highest_consecutive_transaction_by_sender(sender, 0).is_none());
2103        assert!(test_pool.remove_transactions_by_sender(sender).is_empty());
2104        assert_eq!(test_pool.sender_id(&sender), None);
2105    }
2106}