Skip to main content

anvil/eth/pool/
transactions.rs

1use crate::eth::{error::PoolError, util::hex_fmt_many};
2use alloy_consensus::{
3    Transaction, Typed2718,
4    crypto::RecoveryError,
5    transaction::{SignerRecoverable, TxHashRef},
6};
7use alloy_network::AnyRpcTransaction;
8use alloy_primitives::{
9    Address, TxHash,
10    map::{HashMap, HashSet},
11};
12use alloy_rlp::Encodable;
13use anvil_core::eth::transaction::PendingTransaction;
14use parking_lot::RwLock;
15use std::{cmp::Ordering, collections::BTreeSet, fmt, str::FromStr, sync::Arc, time::Instant};
16
17/// A unique identifying marker for a transaction
18pub type TxMarker = Vec<u8>;
19
20/// Result type for replaced transactions: the replaced pool transactions and the hashes they
21/// unlock.
22type ReplacedTransactions<T> = (Vec<Arc<PoolTransaction<T>>>, Vec<TxHash>);
23
24/// creates an unique identifier for aan (`nonce` + `Address`) combo
25pub fn to_marker(nonce: u64, from: Address) -> TxMarker {
26    let mut data = [0u8; 28];
27    data[..8].copy_from_slice(&nonce.to_le_bytes()[..]);
28    data[8..].copy_from_slice(&from.0[..]);
29    data.to_vec()
30}
31
32/// Modes that determine the transaction ordering of the mempool
33///
34/// This type controls the transaction order via the priority metric of a transaction
35#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
36pub enum TransactionOrder {
37    /// Keep the pool transaction transactions sorted in the order they arrive.
38    ///
39    /// This will essentially assign every transaction the exact priority so the order is
40    /// determined by their internal id
41    Fifo,
42    /// This means that it prioritizes transactions based on the fees paid to the miner.
43    #[default]
44    Fees,
45}
46
47impl TransactionOrder {
48    /// Returns the priority of the transactions
49    pub fn priority<T: Transaction>(&self, tx: &T) -> TransactionPriority {
50        match self {
51            Self::Fifo => TransactionPriority::default(),
52            Self::Fees => TransactionPriority(tx.max_fee_per_gas()),
53        }
54    }
55}
56
57impl FromStr for TransactionOrder {
58    type Err = String;
59
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        let s = s.to_lowercase();
62        let order = match s.as_str() {
63            "fees" => Self::Fees,
64            "fifo" => Self::Fifo,
65            _ => return Err(format!("Unknown TransactionOrder: `{s}`")),
66        };
67        Ok(order)
68    }
69}
70
71/// Metric value for the priority of a transaction.
72///
73/// The `TransactionPriority` determines the ordering of two transactions that have all their
74/// markers satisfied.
75#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
76pub struct TransactionPriority(pub u128);
77
78/// Internal Transaction type
79#[derive(Clone, PartialEq, Eq)]
80pub struct PoolTransaction<T> {
81    /// the pending eth transaction
82    pub pending_transaction: PendingTransaction<T>,
83    /// Markers required by the transaction
84    pub requires: Vec<TxMarker>,
85    /// Markers that this transaction provides
86    pub provides: Vec<TxMarker>,
87    /// priority of the transaction
88    pub priority: TransactionPriority,
89    /// Whether this transaction is being replayed from chain history.
90    pub is_replay: bool,
91}
92
93// == impl PoolTransaction ==
94
95impl<T> PoolTransaction<T> {
96    pub const fn new(transaction: PendingTransaction<T>) -> Self {
97        Self {
98            pending_transaction: transaction,
99            requires: vec![],
100            provides: vec![],
101            priority: TransactionPriority(0),
102            is_replay: false,
103        }
104    }
105
106    /// Marks this transaction as a historical replay.
107    pub const fn with_replay(mut self) -> Self {
108        self.is_replay = true;
109        self
110    }
111
112    /// Returns the hash of this transaction
113    pub const fn hash(&self) -> TxHash {
114        *self.pending_transaction.hash()
115    }
116}
117
118impl<T: Transaction> PoolTransaction<T> {
119    /// Returns the max fee per gas of this transaction
120    pub fn max_fee_per_gas(&self) -> u128 {
121        self.pending_transaction.transaction.max_fee_per_gas()
122    }
123}
124
125impl<T: Typed2718> PoolTransaction<T> {
126    /// Returns the type of the transaction
127    pub fn tx_type(&self) -> u8 {
128        self.pending_transaction.transaction.ty()
129    }
130}
131
132impl<T: fmt::Debug> fmt::Debug for PoolTransaction<T> {
133    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
134        write!(fmt, "Transaction {{ ")?;
135        write!(fmt, "hash: {:?}, ", self.pending_transaction.hash())?;
136        write!(fmt, "requires: [{}], ", hex_fmt_many(self.requires.iter()))?;
137        write!(fmt, "provides: [{}], ", hex_fmt_many(self.provides.iter()))?;
138        write!(fmt, "raw tx: {:?}", self.pending_transaction)?;
139        write!(fmt, "}}")?;
140        Ok(())
141    }
142}
143
144impl<T> TryFrom<AnyRpcTransaction> for PoolTransaction<T>
145where
146    T: SignerRecoverable + TxHashRef + Encodable + TryFrom<AnyRpcTransaction>,
147    <T as TryFrom<AnyRpcTransaction>>::Error: Into<eyre::Error>,
148    RecoveryError: Into<eyre::Error>,
149{
150    type Error = eyre::Error;
151    fn try_from(value: AnyRpcTransaction) -> Result<Self, Self::Error> {
152        let typed_transaction = T::try_from(value).map_err(Into::into)?;
153        let pending_transaction = PendingTransaction::new(typed_transaction)?;
154        Ok(Self {
155            pending_transaction,
156            requires: vec![],
157            provides: vec![],
158            priority: TransactionPriority(0),
159            is_replay: false,
160        })
161    }
162}
163
164/// A waiting pool of transaction that are pending, but not yet ready to be included in a new block.
165///
166/// Keeps a set of transactions that are waiting for other transactions
167#[derive(Clone, Debug)]
168pub struct PendingTransactions<T> {
169    /// markers that aren't yet provided by any transaction
170    required_markers: HashMap<TxMarker, HashSet<TxHash>>,
171    /// mapping of the markers of a transaction to the hash of the transaction
172    waiting_markers: HashMap<Vec<TxMarker>, TxHash>,
173    /// the transactions that are not ready yet are waiting for another tx to finish
174    waiting_queue: HashMap<TxHash, PendingPoolTransaction<T>>,
175}
176
177impl<T> Default for PendingTransactions<T> {
178    fn default() -> Self {
179        Self {
180            required_markers: Default::default(),
181            waiting_markers: Default::default(),
182            waiting_queue: Default::default(),
183        }
184    }
185}
186
187impl<T> PendingTransactions<T> {
188    /// Returns the number of transactions that are currently waiting
189    pub fn len(&self) -> usize {
190        self.waiting_queue.len()
191    }
192
193    pub fn is_empty(&self) -> bool {
194        self.waiting_queue.is_empty()
195    }
196
197    /// Clears internal state
198    pub fn clear(&mut self) {
199        self.required_markers.clear();
200        self.waiting_markers.clear();
201        self.waiting_queue.clear();
202    }
203
204    /// Returns an iterator over all transactions in the waiting pool
205    pub fn transactions(&self) -> impl Iterator<Item = Arc<PoolTransaction<T>>> + '_ {
206        self.waiting_queue.values().map(|tx| tx.transaction.clone())
207    }
208
209    /// Returns true if given transaction is part of the queue
210    pub fn contains(&self, hash: &TxHash) -> bool {
211        self.waiting_queue.contains_key(hash)
212    }
213
214    /// Returns the transaction for the hash if it's pending
215    pub fn get(&self, hash: &TxHash) -> Option<&PendingPoolTransaction<T>> {
216        self.waiting_queue.get(hash)
217    }
218
219    /// This will check off the markers of pending transactions.
220    ///
221    /// Returns the those transactions that become unlocked (all markers checked) and can be moved
222    /// to the ready queue.
223    pub fn mark_and_unlock(
224        &mut self,
225        markers: impl IntoIterator<Item = impl AsRef<TxMarker>>,
226    ) -> Vec<PendingPoolTransaction<T>> {
227        let mut unlocked_ready = Vec::new();
228        for mark in markers {
229            let mark = mark.as_ref();
230            if let Some(tx_hashes) = self.required_markers.remove(mark) {
231                for hash in tx_hashes {
232                    let tx = self.waiting_queue.get_mut(&hash).expect("tx is included;");
233                    tx.mark(mark);
234
235                    if tx.is_ready() {
236                        let tx = self.waiting_queue.remove(&hash).expect("tx is included;");
237                        self.waiting_markers.remove(&tx.transaction.provides);
238
239                        unlocked_ready.push(tx);
240                    }
241                }
242            }
243        }
244
245        unlocked_ready
246    }
247
248    /// Removes the transactions associated with the given hashes
249    ///
250    /// Returns all removed transactions.
251    pub fn remove(&mut self, hashes: Vec<TxHash>) -> Vec<Arc<PoolTransaction<T>>> {
252        let mut removed = vec![];
253        for hash in hashes {
254            if let Some(waiting_tx) = self.waiting_queue.remove(&hash) {
255                self.waiting_markers.remove(&waiting_tx.transaction.provides);
256                for marker in waiting_tx.missing_markers {
257                    let remove = if let Some(required) = self.required_markers.get_mut(&marker) {
258                        required.remove(&hash);
259                        required.is_empty()
260                    } else {
261                        false
262                    };
263                    if remove {
264                        self.required_markers.remove(&marker);
265                    }
266                }
267                removed.push(waiting_tx.transaction)
268            }
269        }
270        removed
271    }
272}
273
274impl<T: Transaction> PendingTransactions<T> {
275    /// Adds a transaction to Pending queue of transactions
276    pub fn add_transaction(&mut self, tx: PendingPoolTransaction<T>) -> Result<(), PoolError> {
277        assert!(!tx.is_ready(), "transaction must not be ready");
278        assert!(
279            !self.waiting_queue.contains_key(&tx.transaction.hash()),
280            "transaction is already added"
281        );
282
283        if let Some(replace) = self
284            .waiting_markers
285            .get(&tx.transaction.provides)
286            .and_then(|hash| self.waiting_queue.get(hash))
287        {
288            // check if underpriced
289            if tx.transaction.max_fee_per_gas() <= replace.transaction.max_fee_per_gas() {
290                warn!(target: "txpool", "pending replacement transaction underpriced [{:?}]", tx.transaction.hash());
291                return Err(PoolError::ReplacementUnderpriced(tx.transaction.hash()));
292            }
293        }
294
295        // add all missing markers
296        for marker in &tx.missing_markers {
297            self.required_markers.entry(marker.clone()).or_default().insert(tx.transaction.hash());
298        }
299
300        // also track identifying markers
301        self.waiting_markers.insert(tx.transaction.provides.clone(), tx.transaction.hash());
302        // add tx to the queue
303        self.waiting_queue.insert(tx.transaction.hash(), tx);
304
305        Ok(())
306    }
307}
308
309/// A transaction in the pool
310#[derive(Clone)]
311pub struct PendingPoolTransaction<T> {
312    pub transaction: Arc<PoolTransaction<T>>,
313    /// markers required and have not been satisfied yet by other transactions in the pool
314    pub missing_markers: HashSet<TxMarker>,
315    /// timestamp when the tx was added
316    pub added_at: Instant,
317}
318
319impl<T> PendingPoolTransaction<T> {
320    /// Creates a new `PendingPoolTransaction`.
321    ///
322    /// Determines the markers that are still missing before this transaction can be moved to the
323    /// ready queue.
324    pub fn new(transaction: PoolTransaction<T>, provided: &HashMap<TxMarker, TxHash>) -> Self {
325        let missing_markers = transaction
326            .requires
327            .iter()
328            .filter(|marker| {
329                // is true if the marker is already satisfied either via transaction in the pool
330                !provided.contains_key(&**marker)
331            })
332            .cloned()
333            .collect();
334
335        Self { transaction: Arc::new(transaction), missing_markers, added_at: Instant::now() }
336    }
337
338    /// Removes the required marker
339    pub fn mark(&mut self, marker: &TxMarker) {
340        self.missing_markers.remove(marker);
341    }
342
343    /// Returns true if transaction has all requirements satisfied.
344    pub fn is_ready(&self) -> bool {
345        self.missing_markers.is_empty()
346    }
347}
348
349impl<T: fmt::Debug> fmt::Debug for PendingPoolTransaction<T> {
350    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
351        write!(fmt, "PendingTransaction {{ ")?;
352        write!(fmt, "added_at: {:?}, ", self.added_at)?;
353        write!(fmt, "tx: {:?}, ", self.transaction)?;
354        write!(fmt, "missing_markers: {{{}}}", hex_fmt_many(self.missing_markers.iter()))?;
355        write!(fmt, "}}")
356    }
357}
358
359pub struct TransactionsIterator<T> {
360    all: HashMap<TxHash, ReadyTransaction<T>>,
361    awaiting: HashMap<TxHash, (usize, PoolTransactionRef<T>)>,
362    independent: BTreeSet<PoolTransactionRef<T>>,
363    _invalid: HashSet<TxHash>,
364}
365
366impl<T> TransactionsIterator<T> {
367    /// Depending on number of satisfied requirements insert given ref
368    /// either to awaiting set or to best set.
369    fn independent_or_awaiting(&mut self, satisfied: usize, tx_ref: PoolTransactionRef<T>) {
370        if satisfied >= tx_ref.transaction.requires.len() {
371            // If we have satisfied all deps insert to best
372            self.independent.insert(tx_ref);
373        } else {
374            // otherwise we're still awaiting for some deps
375            self.awaiting.insert(tx_ref.transaction.hash(), (satisfied, tx_ref));
376        }
377    }
378}
379
380impl<T> Iterator for TransactionsIterator<T> {
381    type Item = Arc<PoolTransaction<T>>;
382
383    fn next(&mut self) -> Option<Self::Item> {
384        loop {
385            let best = self.independent.iter().next_back()?.clone();
386            let best = self.independent.take(&best)?;
387            let hash = best.transaction.hash();
388
389            let ready =
390                if let Some(ready) = self.all.get(&hash).cloned() { ready } else { continue };
391
392            // Insert transactions that just got unlocked.
393            for hash in &ready.unlocks {
394                // first check local awaiting transactions
395                let res = if let Some((mut satisfied, tx_ref)) = self.awaiting.remove(hash) {
396                    satisfied += 1;
397                    Some((satisfied, tx_ref))
398                    // then get from the pool
399                } else {
400                    self.all
401                        .get(hash)
402                        .map(|next| (next.requires_offset + 1, next.transaction.clone()))
403                };
404                if let Some((satisfied, tx_ref)) = res {
405                    self.independent_or_awaiting(satisfied, tx_ref)
406                }
407            }
408
409            return Some(best.transaction);
410        }
411    }
412}
413
414/// transactions that are ready to be included in a block.
415#[derive(Clone, Debug)]
416pub struct ReadyTransactions<T> {
417    /// keeps track of transactions inserted in the pool
418    ///
419    /// this way we can determine when transactions where submitted to the pool
420    id: u64,
421    /// markers that are provided by `ReadyTransaction`s
422    provided_markers: HashMap<TxMarker, TxHash>,
423    /// transactions that are ready
424    ready_tx: Arc<RwLock<HashMap<TxHash, ReadyTransaction<T>>>>,
425    /// independent transactions that can be included directly and don't require other transactions
426    /// Sorted by their id
427    independent_transactions: BTreeSet<PoolTransactionRef<T>>,
428}
429
430impl<T> Default for ReadyTransactions<T> {
431    fn default() -> Self {
432        Self {
433            id: 0,
434            provided_markers: Default::default(),
435            ready_tx: Default::default(),
436            independent_transactions: Default::default(),
437        }
438    }
439}
440
441impl<T> ReadyTransactions<T> {
442    /// Returns an iterator over all transactions
443    pub fn get_transactions(&self) -> TransactionsIterator<T> {
444        TransactionsIterator {
445            all: self.ready_tx.read().clone(),
446            independent: self.independent_transactions.clone(),
447            awaiting: Default::default(),
448            _invalid: Default::default(),
449        }
450    }
451
452    /// Clears the internal state
453    pub fn clear(&mut self) {
454        self.provided_markers.clear();
455        self.ready_tx.write().clear();
456        self.independent_transactions.clear();
457    }
458
459    /// Returns true if the transaction is part of the queue.
460    pub fn contains(&self, hash: &TxHash) -> bool {
461        self.ready_tx.read().contains_key(hash)
462    }
463
464    /// Returns the number of ready transactions without cloning the snapshot
465    pub fn len(&self) -> usize {
466        self.ready_tx.read().len()
467    }
468
469    /// Returns true if there are no ready transactions
470    pub fn is_empty(&self) -> bool {
471        self.ready_tx.read().is_empty()
472    }
473
474    /// Returns the transaction for the hash if it's in the ready pool but not yet mined
475    pub fn get(&self, hash: &TxHash) -> Option<ReadyTransaction<T>> {
476        self.ready_tx.read().get(hash).cloned()
477    }
478
479    pub const fn provided_markers(&self) -> &HashMap<TxMarker, TxHash> {
480        &self.provided_markers
481    }
482
483    const fn next_id(&mut self) -> u64 {
484        let id = self.id;
485        self.id = self.id.wrapping_add(1);
486        id
487    }
488
489    /// Removes the transactions from the ready queue and returns the removed transactions.
490    /// This will also remove all transactions that depend on those.
491    pub fn clear_transactions(&mut self, tx_hashes: &[TxHash]) -> Vec<Arc<PoolTransaction<T>>> {
492        self.remove_with_markers(tx_hashes.to_vec(), None)
493    }
494
495    /// Removes the transactions that provide the marker
496    ///
497    /// This will also remove all transactions that lead to the transaction that provides the
498    /// marker.
499    pub fn prune_tags(&mut self, marker: TxMarker) -> Vec<Arc<PoolTransaction<T>>> {
500        let mut removed_tx = vec![];
501
502        // the markers to remove
503        let mut remove = vec![marker];
504
505        while let Some(marker) = remove.pop() {
506            let res = self
507                .provided_markers
508                .remove(&marker)
509                .and_then(|hash| self.ready_tx.write().remove(&hash));
510
511            if let Some(tx) = res {
512                let unlocks = tx.unlocks;
513                self.independent_transactions.remove(&tx.transaction);
514                let tx = tx.transaction.transaction;
515
516                // also prune previous transactions
517                {
518                    let hash = tx.hash();
519                    let mut ready = self.ready_tx.write();
520
521                    let mut previous_markers = |marker| -> Option<Vec<TxMarker>> {
522                        let prev_hash = self.provided_markers.get(marker)?;
523                        let tx2 = ready.get_mut(prev_hash)?;
524                        // remove hash
525                        if let Some(idx) = tx2.unlocks.iter().position(|i| i == &hash) {
526                            tx2.unlocks.swap_remove(idx);
527                        }
528                        tx2.unlocks.is_empty().then(|| tx2.transaction.transaction.provides.clone())
529                    };
530
531                    // find previous transactions
532                    for marker in &tx.requires {
533                        if let Some(mut tags_to_remove) = previous_markers(marker) {
534                            remove.append(&mut tags_to_remove);
535                        }
536                    }
537                }
538
539                // add the transactions that just got unlocked to independent set
540                for hash in unlocks {
541                    if let Some(tx) = self.ready_tx.write().get_mut(&hash) {
542                        tx.requires_offset += 1;
543                        if tx.requires_offset == tx.transaction.transaction.requires.len() {
544                            self.independent_transactions.insert(tx.transaction.clone());
545                        }
546                    }
547                }
548                // finally, remove the markers that this transaction provides
549                let current_marker = &marker;
550                for marker in &tx.provides {
551                    let removed = self.provided_markers.remove(marker);
552                    assert_eq!(
553                        removed,
554                        if current_marker == marker { None } else { Some(tx.hash()) },
555                        "The pool contains exactly one transaction providing given tag; the removed transaction
556						claims to provide that tag, so it has to be mapped to it's hash; qed"
557                    );
558                }
559                removed_tx.push(tx);
560            }
561        }
562
563        removed_tx
564    }
565
566    /// Removes transactions and those that depend on them and satisfy at least one marker in the
567    /// given filter set.
568    pub fn remove_with_markers(
569        &mut self,
570        mut tx_hashes: Vec<TxHash>,
571        marker_filter: Option<HashSet<TxMarker>>,
572    ) -> Vec<Arc<PoolTransaction<T>>> {
573        let mut removed = Vec::new();
574        let mut ready = self.ready_tx.write();
575
576        while let Some(hash) = tx_hashes.pop() {
577            if let Some(mut tx) = ready.remove(&hash) {
578                let invalidated = tx.transaction.transaction.provides.iter().filter(|mark| {
579                    marker_filter.as_ref().map(|filter| !filter.contains(&**mark)).unwrap_or(true)
580                });
581
582                let mut removed_some_marks = false;
583                // remove entries from provided_markers
584                for mark in invalidated {
585                    removed_some_marks = true;
586                    self.provided_markers.remove(mark);
587                }
588
589                // remove from unlocks
590                for mark in &tx.transaction.transaction.requires {
591                    if let Some(provider_hash) = self.provided_markers.get(mark)
592                        && let Some(provider_tx) = ready.get_mut(provider_hash)
593                        && let Some(idx) = provider_tx.unlocks.iter().position(|i| i == &hash)
594                    {
595                        provider_tx.unlocks.swap_remove(idx);
596                    }
597                }
598
599                // remove from the independent set
600                self.independent_transactions.remove(&tx.transaction);
601
602                if removed_some_marks {
603                    // remove all transactions that the current one unlocks
604                    tx_hashes.append(&mut tx.unlocks);
605                }
606
607                // remove transaction
608                removed.push(tx.transaction.transaction);
609            }
610        }
611
612        removed
613    }
614}
615
616impl<T: Transaction> ReadyTransactions<T> {
617    /// Adds a new transactions to the ready queue.
618    ///
619    /// # Panics
620    ///
621    /// If the pending transaction is not ready ([`PendingPoolTransaction::is_ready`])
622    /// or the transaction is already included.
623    pub fn add_transaction(
624        &mut self,
625        tx: PendingPoolTransaction<T>,
626    ) -> Result<Vec<Arc<PoolTransaction<T>>>, PoolError> {
627        assert!(tx.is_ready(), "transaction must be ready",);
628        assert!(
629            !self.ready_tx.read().contains_key(&tx.transaction.hash()),
630            "transaction already included"
631        );
632
633        let (replaced_tx, unlocks) = self.replaced_transactions(&tx.transaction)?;
634
635        let id = self.next_id();
636        let hash = tx.transaction.hash();
637
638        let mut independent = true;
639        let mut requires_offset = 0;
640        let mut ready = self.ready_tx.write();
641        // Add links to transactions that unlock the current one
642        for mark in &tx.transaction.requires {
643            // Check if the transaction that satisfies the mark is still in the queue.
644            if let Some(other) = self.provided_markers.get(mark) {
645                let tx = ready.get_mut(other).expect("hash included;");
646                tx.unlocks.push(hash);
647                // tx still depends on other tx
648                independent = false;
649            } else {
650                requires_offset += 1;
651            }
652        }
653
654        // update markers
655        for mark in tx.transaction.provides.iter().cloned() {
656            self.provided_markers.insert(mark, hash);
657        }
658
659        let transaction = PoolTransactionRef { id, transaction: tx.transaction };
660
661        // add to the independent set
662        if independent {
663            self.independent_transactions.insert(transaction.clone());
664        }
665
666        // insert to ready queue
667        ready.insert(hash, ReadyTransaction { transaction, unlocks, requires_offset });
668
669        Ok(replaced_tx)
670    }
671
672    /// Removes and returns those transactions that got replaced by the `tx`
673    fn replaced_transactions(
674        &mut self,
675        tx: &PoolTransaction<T>,
676    ) -> Result<ReplacedTransactions<T>, PoolError> {
677        // check if we are replacing transactions
678        let remove_hashes: HashSet<_> =
679            tx.provides.iter().filter_map(|mark| self.provided_markers.get(mark)).collect();
680
681        // early exit if we are not replacing anything.
682        if remove_hashes.is_empty() {
683            return Ok((Vec::new(), Vec::new()));
684        }
685
686        // check if we're replacing the same transaction and if it can be replaced
687        let mut unlocked_tx = Vec::new();
688        {
689            // construct a list of unlocked transactions
690            // also check for transactions that shouldn't be replaced because underpriced
691            let ready = self.ready_tx.read();
692            for to_remove in remove_hashes.iter().filter_map(|hash| ready.get(*hash)) {
693                // if we're attempting to replace a transaction that provides the exact same markers
694                // (addr + nonce) then we check for gas price
695                if to_remove.provides() == tx.provides {
696                    // check if underpriced
697                    if tx.pending_transaction.transaction.max_fee_per_gas()
698                        <= to_remove.max_fee_per_gas()
699                    {
700                        warn!(target: "txpool", "ready replacement transaction underpriced [{:?}]", tx.hash());
701                        return Err(PoolError::ReplacementUnderpriced(tx.hash()));
702                    }
703                    trace!(target: "txpool", "replacing ready transaction [{:?}] with higher gas price [{:?}]", to_remove.transaction.transaction.hash(), tx.hash());
704                }
705
706                unlocked_tx.extend(to_remove.unlocks.iter().copied())
707            }
708        }
709
710        let remove_hashes = remove_hashes.into_iter().copied().collect::<Vec<_>>();
711
712        let new_provides = tx.provides.iter().cloned().collect::<HashSet<_>>();
713        let removed_tx = self.remove_with_markers(remove_hashes, Some(new_provides));
714
715        Ok((removed_tx, unlocked_tx))
716    }
717}
718
719/// A reference to a transaction in the pool
720#[derive(Debug)]
721pub struct PoolTransactionRef<T> {
722    /// actual transaction
723    pub transaction: Arc<PoolTransaction<T>>,
724    /// identifier used to internally compare the transaction in the pool
725    pub id: u64,
726}
727
728impl<T> Clone for PoolTransactionRef<T> {
729    fn clone(&self) -> Self {
730        Self { transaction: Arc::clone(&self.transaction), id: self.id }
731    }
732}
733
734impl<T> Eq for PoolTransactionRef<T> {}
735
736impl<T> PartialEq<Self> for PoolTransactionRef<T> {
737    fn eq(&self, other: &Self) -> bool {
738        self.cmp(other) == Ordering::Equal
739    }
740}
741
742impl<T> PartialOrd<Self> for PoolTransactionRef<T> {
743    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
744        Some(self.cmp(other))
745    }
746}
747
748impl<T> Ord for PoolTransactionRef<T> {
749    fn cmp(&self, other: &Self) -> Ordering {
750        self.transaction
751            .priority
752            .cmp(&other.transaction.priority)
753            .then_with(|| other.id.cmp(&self.id))
754    }
755}
756
757#[derive(Debug)]
758pub struct ReadyTransaction<T> {
759    /// ref to the actual transaction
760    pub transaction: PoolTransactionRef<T>,
761    /// tracks the transactions that get unlocked by this transaction
762    pub unlocks: Vec<TxHash>,
763    /// amount of required markers that are inherently provided
764    pub requires_offset: usize,
765}
766
767impl<T> Clone for ReadyTransaction<T> {
768    fn clone(&self) -> Self {
769        Self {
770            transaction: self.transaction.clone(),
771            unlocks: self.unlocks.clone(),
772            requires_offset: self.requires_offset,
773        }
774    }
775}
776
777impl<T> ReadyTransaction<T> {
778    pub fn provides(&self) -> &[TxMarker] {
779        &self.transaction.transaction.provides
780    }
781}
782
783impl<T: Transaction> ReadyTransaction<T> {
784    pub fn max_fee_per_gas(&self) -> u128 {
785        self.transaction.transaction.max_fee_per_gas()
786    }
787}
788
789#[cfg(test)]
790mod tests {
791    use super::*;
792
793    #[test]
794    fn can_id_txs() {
795        let addr = Address::random();
796        assert_eq!(to_marker(1, addr), to_marker(1, addr));
797        assert_ne!(to_marker(2, addr), to_marker(1, addr));
798    }
799}