reth_transaction_pool/
ordering.rs

1use crate::traits::PoolTransaction;
2use alloy_primitives::U256;
3use std::{cmp::Ordering, fmt::Debug, marker::PhantomData};
4
5/// Priority of the transaction that can be missing.
6///
7/// Transactions with missing priorities are ranked lower.
8#[derive(PartialEq, Eq, Clone, Debug)]
9pub enum Priority<T: Ord + Clone> {
10    /// The value of the priority of the transaction.
11    Value(T),
12    /// Missing priority due to ordering internals.
13    None,
14}
15
16impl<T: Ord + Clone> From<Option<T>> for Priority<T> {
17    fn from(value: Option<T>) -> Self {
18        value.map_or(Self::None, Priority::Value)
19    }
20}
21
22impl<T: Ord + Clone> PartialOrd for Priority<T> {
23    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
24        Some(self.cmp(other))
25    }
26}
27
28impl<T: Ord + Clone> Ord for Priority<T> {
29    fn cmp(&self, other: &Self) -> Ordering {
30        match (self, other) {
31            (Self::Value(a), Self::Value(b)) => a.cmp(b),
32            // Note: None should be smaller than Value.
33            (Self::Value(_), Self::None) => Ordering::Greater,
34            (Self::None, Self::Value(_)) => Ordering::Less,
35            (Self::None, Self::None) => Ordering::Equal,
36        }
37    }
38}
39
40/// Transaction ordering trait to determine the order of transactions.
41///
42/// Decides how transactions should be ordered within the pool, depending on a `Priority` value.
43///
44/// The returned priority must reflect [total order](https://en.wikipedia.org/wiki/Total_order).
45pub trait TransactionOrdering: Debug + Send + Sync + 'static {
46    /// Priority of a transaction.
47    ///
48    /// Higher is better.
49    type PriorityValue: Ord + Clone + Default + Debug + Send + Sync;
50
51    /// The transaction type to determine the priority of.
52    type Transaction: PoolTransaction;
53
54    /// Returns the priority score for the given transaction.
55    fn priority(
56        &self,
57        transaction: &Self::Transaction,
58        base_fee: u64,
59    ) -> Priority<Self::PriorityValue>;
60}
61
62/// Default ordering for the pool.
63///
64/// The transactions are ordered by their coinbase tip.
65/// The higher the coinbase tip is, the higher the priority of the transaction.
66#[derive(Debug)]
67#[non_exhaustive]
68pub struct CoinbaseTipOrdering<T>(PhantomData<T>);
69
70impl<T> TransactionOrdering for CoinbaseTipOrdering<T>
71where
72    T: PoolTransaction + 'static,
73{
74    type PriorityValue = U256;
75    type Transaction = T;
76
77    /// Source: <https://github.com/ethereum/go-ethereum/blob/7f756dc1185d7f1eeeacb1d12341606b7135f9ea/core/txpool/legacypool/list.go#L469-L482>.
78    ///
79    /// NOTE: The implementation is incomplete for missing base fee.
80    fn priority(
81        &self,
82        transaction: &Self::Transaction,
83        base_fee: u64,
84    ) -> Priority<Self::PriorityValue> {
85        transaction.effective_tip_per_gas(base_fee).map(U256::from).into()
86    }
87}
88
89impl<T> Default for CoinbaseTipOrdering<T> {
90    fn default() -> Self {
91        Self(Default::default())
92    }
93}
94
95impl<T> Clone for CoinbaseTipOrdering<T> {
96    fn clone(&self) -> Self {
97        Self::default()
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_priority_ordering() {
107        let p1 = Priority::Value(3);
108        let p2 = Priority::Value(1);
109        let p3 = Priority::None;
110
111        assert!(p1 > p2); // 3 > 1
112        assert!(p1 > p3); // Value(3) > None
113        assert!(p2 > p3); // Value(1) > None
114        assert_eq!(p3, Priority::None);
115    }
116}