use crate::{
error::{Eip4844PoolTransactionError, InvalidPoolTransactionError},
identifier::{SenderId, TransactionId},
pool::pending::PendingTransaction,
PoolTransaction, TransactionOrdering, ValidPoolTransaction,
};
use alloy_primitives::Address;
use core::fmt;
use reth_payload_util::PayloadTransactions;
use reth_primitives::{InvalidTransactionError, RecoveredTx};
use std::{
collections::{BTreeMap, BTreeSet, HashSet, VecDeque},
sync::Arc,
};
use tokio::sync::broadcast::{error::TryRecvError, Receiver};
use tracing::debug;
pub(crate) struct BestTransactionsWithFees<T: TransactionOrdering> {
pub(crate) best: BestTransactions<T>,
pub(crate) base_fee: u64,
pub(crate) base_fee_per_blob_gas: u64,
}
impl<T: TransactionOrdering> crate::traits::BestTransactions for BestTransactionsWithFees<T> {
fn mark_invalid(&mut self, tx: &Self::Item, kind: InvalidPoolTransactionError) {
BestTransactions::mark_invalid(&mut self.best, tx, kind)
}
fn no_updates(&mut self) {
self.best.no_updates()
}
fn skip_blobs(&mut self) {
self.set_skip_blobs(true)
}
fn set_skip_blobs(&mut self, skip_blobs: bool) {
self.best.set_skip_blobs(skip_blobs)
}
}
impl<T: TransactionOrdering> Iterator for BestTransactionsWithFees<T> {
type Item = Arc<ValidPoolTransaction<T::Transaction>>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let best = Iterator::next(&mut self.best)?;
if best.transaction.max_fee_per_gas() >= self.base_fee as u128 &&
best.transaction
.max_fee_per_blob_gas()
.is_none_or(|fee| fee >= self.base_fee_per_blob_gas as u128)
{
return Some(best);
}
crate::traits::BestTransactions::mark_invalid(
self,
&best,
InvalidPoolTransactionError::Underpriced,
);
}
}
}
#[derive(Debug)]
pub struct BestTransactions<T: TransactionOrdering> {
pub(crate) all: BTreeMap<TransactionId, PendingTransaction<T>>,
pub(crate) independent: BTreeSet<PendingTransaction<T>>,
pub(crate) invalid: HashSet<SenderId>,
pub(crate) new_transaction_receiver: Option<Receiver<PendingTransaction<T>>>,
pub(crate) skip_blobs: bool,
}
impl<T: TransactionOrdering> BestTransactions<T> {
pub(crate) fn mark_invalid(
&mut self,
tx: &Arc<ValidPoolTransaction<T::Transaction>>,
_kind: InvalidPoolTransactionError,
) {
self.invalid.insert(tx.sender_id());
}
pub(crate) fn ancestor(&self, id: &TransactionId) -> Option<&PendingTransaction<T>> {
self.all.get(&id.unchecked_ancestor()?)
}
fn try_recv(&mut self) -> Option<PendingTransaction<T>> {
loop {
match self.new_transaction_receiver.as_mut()?.try_recv() {
Ok(tx) => return Some(tx),
Err(TryRecvError::Lagged(_)) => {
continue
}
Err(_) => return None,
}
}
}
fn pop_best(&mut self) -> Option<PendingTransaction<T>> {
self.independent.pop_last().inspect(|best| {
let removed = self.all.remove(best.transaction.id());
debug_assert!(removed.is_some(), "must be present in both sets");
})
}
fn add_new_transactions(&mut self) {
while let Some(pending_tx) = self.try_recv() {
let tx_id = *pending_tx.transaction.id();
if self.ancestor(&tx_id).is_none() {
self.independent.insert(pending_tx.clone());
}
self.all.insert(tx_id, pending_tx);
}
}
}
impl<T: TransactionOrdering> crate::traits::BestTransactions for BestTransactions<T> {
fn mark_invalid(&mut self, tx: &Self::Item, kind: InvalidPoolTransactionError) {
Self::mark_invalid(self, tx, kind)
}
fn no_updates(&mut self) {
self.new_transaction_receiver.take();
}
fn skip_blobs(&mut self) {
self.set_skip_blobs(true);
}
fn set_skip_blobs(&mut self, skip_blobs: bool) {
self.skip_blobs = skip_blobs;
}
}
impl<T: TransactionOrdering> Iterator for BestTransactions<T> {
type Item = Arc<ValidPoolTransaction<T::Transaction>>;
fn next(&mut self) -> Option<Self::Item> {
loop {
self.add_new_transactions();
let best = self.pop_best()?;
let sender_id = best.transaction.sender_id();
if self.invalid.contains(&sender_id) {
debug!(
target: "txpool",
"[{:?}] skipping invalid transaction",
best.transaction.hash()
);
continue
}
if let Some(unlocked) = self.all.get(&best.unlocks()) {
self.independent.insert(unlocked.clone());
}
if self.skip_blobs && best.transaction.transaction.is_eip4844() {
self.mark_invalid(
&best.transaction,
InvalidPoolTransactionError::Eip4844(
Eip4844PoolTransactionError::NoEip4844Blobs,
),
)
} else {
return Some(best.transaction)
}
}
}
}
#[derive(Debug)]
pub struct BestPayloadTransactions<T, I>
where
T: PoolTransaction,
I: Iterator<Item = Arc<ValidPoolTransaction<T>>>,
{
invalid: HashSet<Address>,
best: I,
}
impl<T, I> BestPayloadTransactions<T, I>
where
T: PoolTransaction,
I: Iterator<Item = Arc<ValidPoolTransaction<T>>>,
{
pub fn new(best: I) -> Self {
Self { invalid: Default::default(), best }
}
}
impl<T, I> PayloadTransactions for BestPayloadTransactions<T, I>
where
T: PoolTransaction,
I: Iterator<Item = Arc<ValidPoolTransaction<T>>>,
{
type Transaction = T::Consensus;
fn next(&mut self, _ctx: ()) -> Option<RecoveredTx<Self::Transaction>> {
loop {
let tx = self.best.next()?;
if self.invalid.contains(&tx.sender()) {
continue
}
return Some(tx.to_consensus())
}
}
fn mark_invalid(&mut self, sender: Address, _nonce: u64) {
self.invalid.insert(sender);
}
}
pub struct BestTransactionFilter<I, P> {
pub(crate) best: I,
pub(crate) predicate: P,
}
impl<I, P> BestTransactionFilter<I, P> {
pub const fn new(best: I, predicate: P) -> Self {
Self { best, predicate }
}
}
impl<I, P> Iterator for BestTransactionFilter<I, P>
where
I: crate::traits::BestTransactions,
P: FnMut(&<I as Iterator>::Item) -> bool,
{
type Item = <I as Iterator>::Item;
fn next(&mut self) -> Option<Self::Item> {
loop {
let best = self.best.next()?;
if (self.predicate)(&best) {
return Some(best)
}
self.best.mark_invalid(
&best,
InvalidPoolTransactionError::Consensus(InvalidTransactionError::TxTypeNotSupported),
);
}
}
}
impl<I, P> crate::traits::BestTransactions for BestTransactionFilter<I, P>
where
I: crate::traits::BestTransactions,
P: FnMut(&<I as Iterator>::Item) -> bool + Send,
{
fn mark_invalid(&mut self, tx: &Self::Item, kind: InvalidPoolTransactionError) {
crate::traits::BestTransactions::mark_invalid(&mut self.best, tx, kind)
}
fn no_updates(&mut self) {
self.best.no_updates()
}
fn skip_blobs(&mut self) {
self.set_skip_blobs(true)
}
fn set_skip_blobs(&mut self, skip_blobs: bool) {
self.best.set_skip_blobs(skip_blobs)
}
}
impl<I: fmt::Debug, P> fmt::Debug for BestTransactionFilter<I, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BestTransactionFilter").field("best", &self.best).finish()
}
}
#[derive(Debug)]
pub struct BestTransactionsWithPrioritizedSenders<I: Iterator> {
inner: I,
prioritized_senders: HashSet<Address>,
max_prioritized_gas: u64,
buffer: VecDeque<I::Item>,
prioritized_gas: u64,
}
impl<I: Iterator> BestTransactionsWithPrioritizedSenders<I> {
pub fn new(prioritized_senders: HashSet<Address>, max_prioritized_gas: u64, inner: I) -> Self {
Self {
inner,
prioritized_senders,
max_prioritized_gas,
buffer: Default::default(),
prioritized_gas: Default::default(),
}
}
}
impl<I, T> Iterator for BestTransactionsWithPrioritizedSenders<I>
where
I: crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T>>>,
T: PoolTransaction,
{
type Item = <I as Iterator>::Item;
fn next(&mut self) -> Option<Self::Item> {
if self.prioritized_gas < self.max_prioritized_gas {
for item in &mut self.inner {
if self.prioritized_senders.contains(&item.transaction.sender()) &&
self.prioritized_gas + item.transaction.gas_limit() <=
self.max_prioritized_gas
{
self.prioritized_gas += item.transaction.gas_limit();
return Some(item)
}
self.buffer.push_back(item);
}
}
if let Some(item) = self.buffer.pop_front() {
Some(item)
} else {
self.inner.next()
}
}
}
impl<I, T> crate::traits::BestTransactions for BestTransactionsWithPrioritizedSenders<I>
where
I: crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T>>>,
T: PoolTransaction,
{
fn mark_invalid(&mut self, tx: &Self::Item, kind: InvalidPoolTransactionError) {
self.inner.mark_invalid(tx, kind)
}
fn no_updates(&mut self) {
self.inner.no_updates()
}
fn set_skip_blobs(&mut self, skip_blobs: bool) {
if skip_blobs {
self.buffer.retain(|tx| !tx.transaction.is_eip4844())
}
self.inner.set_skip_blobs(skip_blobs)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
pool::pending::PendingPool,
test_utils::{MockOrdering, MockTransaction, MockTransactionFactory},
BestTransactions, Priority,
};
use alloy_primitives::U256;
use reth_payload_util::{PayloadTransactionsChain, PayloadTransactionsFixed};
#[test]
fn test_best_iter() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 10;
let tx = MockTransaction::eip1559();
for nonce in 0..num_tx {
let tx = tx.clone().rng_hash().with_nonce(nonce);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best();
assert_eq!(best.all.len(), num_tx as usize);
assert_eq!(best.independent.len(), 1);
for nonce in 0..num_tx {
assert_eq!(best.independent.len(), 1);
let tx = best.next().unwrap();
assert_eq!(tx.nonce(), nonce);
}
}
#[test]
fn test_best_iter_invalid() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 10;
let tx = MockTransaction::eip1559();
for nonce in 0..num_tx {
let tx = tx.clone().rng_hash().with_nonce(nonce);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best();
let invalid = best.independent.iter().next().unwrap();
best.mark_invalid(
&invalid.transaction.clone(),
InvalidPoolTransactionError::Consensus(InvalidTransactionError::TxTypeNotSupported),
);
assert!(best.next().is_none());
}
#[test]
fn test_best_transactions_iter_invalid() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 10;
let tx = MockTransaction::eip1559();
for nonce in 0..num_tx {
let tx = tx.clone().rng_hash().with_nonce(nonce);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best: Box<
dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<MockTransaction>>>,
> = Box::new(pool.best());
let tx = Iterator::next(&mut best).unwrap();
crate::traits::BestTransactions::mark_invalid(
&mut *best,
&tx,
InvalidPoolTransactionError::Consensus(InvalidTransactionError::TxTypeNotSupported),
);
assert!(Iterator::next(&mut best).is_none());
}
#[test]
fn test_best_with_fees_iter_base_fee_satisfied() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 5;
let base_fee: u64 = 10;
let base_fee_per_blob_gas: u64 = 15;
for nonce in 0..num_tx {
let tx = MockTransaction::eip1559()
.rng_hash()
.with_nonce(nonce)
.with_max_fee(base_fee as u128 + 5);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best_with_basefee_and_blobfee(base_fee, base_fee_per_blob_gas);
for nonce in 0..num_tx {
let tx = best.next().expect("Transaction should be returned");
assert_eq!(tx.nonce(), nonce);
assert!(tx.transaction.max_fee_per_gas() >= base_fee as u128);
}
}
#[test]
fn test_best_with_fees_iter_base_fee_violated() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 5;
let base_fee: u64 = 20;
let base_fee_per_blob_gas: u64 = 15;
for nonce in 0..num_tx {
let tx = MockTransaction::eip1559()
.rng_hash()
.with_nonce(nonce)
.with_max_fee(base_fee as u128 - 5);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best_with_basefee_and_blobfee(base_fee, base_fee_per_blob_gas);
assert!(best.next().is_none());
}
#[test]
fn test_best_with_fees_iter_blob_fee_satisfied() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 5;
let base_fee: u64 = 10;
let base_fee_per_blob_gas: u64 = 20;
for nonce in 0..num_tx {
let tx = MockTransaction::eip4844()
.rng_hash()
.with_nonce(nonce)
.with_max_fee(base_fee as u128 + 5)
.with_blob_fee(base_fee_per_blob_gas as u128 + 5);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best_with_basefee_and_blobfee(base_fee, base_fee_per_blob_gas);
for nonce in 0..num_tx {
let tx = best.next().expect("Transaction should be returned");
assert_eq!(tx.nonce(), nonce);
assert!(tx.transaction.max_fee_per_gas() >= base_fee as u128);
assert!(
tx.transaction.max_fee_per_blob_gas().unwrap() >= base_fee_per_blob_gas as u128
);
}
assert!(best.next().is_none());
}
#[test]
fn test_best_with_fees_iter_blob_fee_violated() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 5;
let base_fee: u64 = 10;
let base_fee_per_blob_gas: u64 = 20;
for nonce in 0..num_tx {
let tx = MockTransaction::eip4844()
.rng_hash()
.with_nonce(nonce)
.with_max_fee(base_fee as u128 + 5)
.with_blob_fee(base_fee_per_blob_gas as u128 - 5);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best_with_basefee_and_blobfee(base_fee, base_fee_per_blob_gas);
assert!(best.next().is_none());
}
#[test]
fn test_best_with_fees_iter_mixed_fees() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let base_fee: u64 = 10;
let base_fee_per_blob_gas: u64 = 20;
let tx1 =
MockTransaction::eip1559().rng_hash().with_nonce(0).with_max_fee(base_fee as u128 + 5);
let tx2 = MockTransaction::eip4844()
.rng_hash()
.with_nonce(1)
.with_max_fee(base_fee as u128 + 5)
.with_blob_fee(base_fee_per_blob_gas as u128 + 5);
let tx3 = MockTransaction::eip4844()
.rng_hash()
.with_nonce(2)
.with_max_fee(base_fee as u128 + 5)
.with_blob_fee(base_fee_per_blob_gas as u128 - 5);
let tx4 =
MockTransaction::eip1559().rng_hash().with_nonce(3).with_max_fee(base_fee as u128 - 5);
pool.add_transaction(Arc::new(f.validated(tx1.clone())), 0);
pool.add_transaction(Arc::new(f.validated(tx2.clone())), 0);
pool.add_transaction(Arc::new(f.validated(tx3)), 0);
pool.add_transaction(Arc::new(f.validated(tx4)), 0);
let mut best = pool.best_with_basefee_and_blobfee(base_fee, base_fee_per_blob_gas);
let expected_order = vec![tx1, tx2];
for expected_tx in expected_order {
let tx = best.next().expect("Transaction should be returned");
assert_eq!(tx.transaction, expected_tx);
}
assert!(best.next().is_none());
}
#[test]
fn test_best_add_transaction_with_next_nonce() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 5;
let tx = MockTransaction::eip1559();
for nonce in 0..num_tx {
let tx = tx.clone().rng_hash().with_nonce(nonce);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best();
let (tx_sender, tx_receiver) =
tokio::sync::broadcast::channel::<PendingTransaction<MockOrdering>>(1000);
best.new_transaction_receiver = Some(tx_receiver);
let new_tx = MockTransaction::eip1559().rng_hash().with_nonce(5);
let valid_new_tx = f.validated(new_tx);
let pending_tx = PendingTransaction {
submission_id: 10,
transaction: Arc::new(valid_new_tx.clone()),
priority: Priority::Value(U256::from(1000)),
};
tx_sender.send(pending_tx.clone()).unwrap();
best.add_new_transactions();
assert_eq!(best.all.len(), 6);
assert!(best.all.contains_key(valid_new_tx.id()));
assert_eq!(best.independent.len(), 2);
assert!(best.independent.contains(&pending_tx));
}
#[test]
fn test_best_add_transaction_with_ancestor() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 5;
let tx = MockTransaction::eip1559();
for nonce in 0..num_tx {
let tx = tx.clone().rng_hash().with_nonce(nonce);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best();
let (tx_sender, tx_receiver) =
tokio::sync::broadcast::channel::<PendingTransaction<MockOrdering>>(1000);
best.new_transaction_receiver = Some(tx_receiver);
let base_tx1 = MockTransaction::eip1559().rng_hash().with_nonce(5);
let valid_new_tx1 = f.validated(base_tx1.clone());
let pending_tx1 = PendingTransaction {
submission_id: 10,
transaction: Arc::new(valid_new_tx1.clone()),
priority: Priority::Value(U256::from(1000)),
};
tx_sender.send(pending_tx1.clone()).unwrap();
best.add_new_transactions();
assert_eq!(best.all.len(), 6);
assert!(best.all.contains_key(valid_new_tx1.id()));
assert_eq!(best.independent.len(), 2);
assert!(best.independent.contains(&pending_tx1));
let base_tx2 = base_tx1.with_nonce(6);
let valid_new_tx2 = f.validated(base_tx2);
let pending_tx2 = PendingTransaction {
submission_id: 11, transaction: Arc::new(valid_new_tx2.clone()),
priority: Priority::Value(U256::from(1000)),
};
tx_sender.send(pending_tx2.clone()).unwrap();
best.add_new_transactions();
assert_eq!(best.all.len(), 7);
assert!(best.all.contains_key(valid_new_tx2.id()));
assert_eq!(best.independent.len(), 2);
assert!(!best.independent.contains(&pending_tx2));
}
#[test]
fn test_best_transactions_filter_trait_object() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let num_tx = 5;
let tx = MockTransaction::eip1559();
for nonce in 0..num_tx {
let tx = tx.clone().rng_hash().with_nonce(nonce);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let best: Box<dyn crate::traits::BestTransactions<Item = _>> = Box::new(pool.best());
let filter =
BestTransactionFilter::new(best, |tx: &Arc<ValidPoolTransaction<MockTransaction>>| {
tx.nonce() % 2 == 0
});
for tx in filter {
assert_eq!(tx.nonce() % 2, 0);
}
}
#[test]
fn test_best_transactions_prioritized_senders() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
for gas_price in 0..5 {
let tx = MockTransaction::eip1559().with_gas_price(gas_price);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let prioritized_tx = MockTransaction::eip1559().with_gas_price(0);
let valid_prioritized_tx = f.validated(prioritized_tx.clone());
pool.add_transaction(Arc::new(valid_prioritized_tx), 0);
let prioritized_senders = HashSet::from([prioritized_tx.sender()]);
let best =
BestTransactionsWithPrioritizedSenders::new(prioritized_senders, 200, pool.best());
let mut iter = best.into_iter();
let top_of_block_tx = iter.next().unwrap();
assert_eq!(top_of_block_tx.max_fee_per_gas(), 0);
assert_eq!(top_of_block_tx.sender(), prioritized_tx.sender());
for gas_price in (0..5).rev() {
assert_eq!(iter.next().unwrap().max_fee_per_gas(), gas_price);
}
}
#[test]
fn test_best_transactions_chained_iterators() {
let mut priority_pool = PendingPool::new(MockOrdering::default());
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let address_top_of_block = Address::random();
let address_in_priority_pool = Address::random();
let address_a = Address::random();
let address_b = Address::random();
let address_regular = Address::random();
{
let prioritized_tx_a =
MockTransaction::eip1559().with_gas_price(5).with_sender(address_a);
let prioritized_tx_b =
MockTransaction::eip1559().with_gas_price(10).with_sender(address_b);
let regular_tx =
MockTransaction::eip1559().with_gas_price(15).with_sender(address_regular);
pool.add_transaction(Arc::new(f.validated(prioritized_tx_a)), 0);
pool.add_transaction(Arc::new(f.validated(prioritized_tx_b)), 0);
pool.add_transaction(Arc::new(f.validated(regular_tx)), 0);
}
{
let prioritized_tx =
MockTransaction::eip1559().with_gas_price(0).with_sender(address_in_priority_pool);
let valid_prioritized_tx = f.validated(prioritized_tx);
priority_pool.add_transaction(Arc::new(valid_prioritized_tx), 0);
}
let mut block = PayloadTransactionsChain::new(
PayloadTransactionsFixed::single(
MockTransaction::eip1559().with_sender(address_top_of_block).into(),
),
Some(100),
PayloadTransactionsChain::new(
BestPayloadTransactions::new(priority_pool.best()),
Some(100),
BestPayloadTransactions::new(BestTransactionsWithPrioritizedSenders::new(
HashSet::from([address_a]),
200,
BestTransactionsWithPrioritizedSenders::new(
HashSet::from([address_b]),
200,
pool.best(),
),
)),
None,
),
None,
);
assert_eq!(block.next(()).unwrap().signer(), address_top_of_block);
assert_eq!(block.next(()).unwrap().signer(), address_in_priority_pool);
assert_eq!(block.next(()).unwrap().signer(), address_a);
assert_eq!(block.next(()).unwrap().signer(), address_b);
assert_eq!(block.next(()).unwrap().signer(), address_regular);
}
#[test]
fn test_best_with_fees_iter_no_blob_fee_required() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let base_fee: u64 = 10;
let base_fee_per_blob_gas: u64 = 0; for nonce in 0..5 {
let tx = MockTransaction::eip1559()
.rng_hash()
.with_nonce(nonce)
.with_max_fee(base_fee as u128 + 5);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
}
let mut best = pool.best_with_basefee_and_blobfee(base_fee, base_fee_per_blob_gas);
for nonce in 0..5 {
let tx = best.next().expect("Transaction should be returned");
assert_eq!(tx.nonce(), nonce);
}
assert!(best.next().is_none());
}
#[test]
fn test_best_with_fees_iter_mix_of_blob_and_non_blob_transactions() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let base_fee: u64 = 10;
let base_fee_per_blob_gas: u64 = 15;
let tx_non_blob =
MockTransaction::eip1559().rng_hash().with_nonce(0).with_max_fee(base_fee as u128 + 5);
pool.add_transaction(Arc::new(f.validated(tx_non_blob.clone())), 0);
let tx_blob = MockTransaction::eip4844()
.rng_hash()
.with_nonce(1)
.with_max_fee(base_fee as u128 + 5)
.with_blob_fee(base_fee_per_blob_gas as u128 + 5);
pool.add_transaction(Arc::new(f.validated(tx_blob.clone())), 0);
let mut best = pool.best_with_basefee_and_blobfee(base_fee, base_fee_per_blob_gas);
let tx = best.next().expect("Transaction should be returned");
assert_eq!(tx.transaction, tx_non_blob);
let tx = best.next().expect("Transaction should be returned");
assert_eq!(tx.transaction, tx_blob);
assert!(best.next().is_none());
}
#[test]
fn test_best_transactions_with_skipping_blobs() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let tx_blob = MockTransaction::eip4844().rng_hash().with_nonce(0).with_blob_fee(100);
let valid_blob_tx = f.validated(tx_blob);
pool.add_transaction(Arc::new(valid_blob_tx), 0);
let tx_non_blob = MockTransaction::eip1559().rng_hash().with_nonce(1).with_max_fee(200);
let valid_non_blob_tx = f.validated(tx_non_blob.clone());
pool.add_transaction(Arc::new(valid_non_blob_tx), 0);
let mut best = pool.best();
best.skip_blobs();
let tx = best.next().expect("Transaction should be returned");
assert_eq!(tx.transaction, tx_non_blob);
assert!(best.next().is_none());
}
#[test]
fn test_best_transactions_no_updates() {
let mut pool = PendingPool::new(MockOrdering::default());
let mut f = MockTransactionFactory::default();
let tx = MockTransaction::eip1559().rng_hash().with_nonce(0).with_max_fee(100);
let valid_tx = f.validated(tx);
pool.add_transaction(Arc::new(valid_tx), 0);
let mut best = pool.best();
let (_tx_sender, tx_receiver) =
tokio::sync::broadcast::channel::<PendingTransaction<MockOrdering>>(1000);
best.new_transaction_receiver = Some(tx_receiver);
assert!(best.new_transaction_receiver.is_some());
best.no_updates();
assert!(best.new_transaction_receiver.is_none());
}
}