diff --git a/crates/transaction-pool/src/pool/best.rs b/crates/transaction-pool/src/pool/best.rs index 4e874f5c7a..027dfa6b6c 100644 --- a/crates/transaction-pool/src/pool/best.rs +++ b/crates/transaction-pool/src/pool/best.rs @@ -17,13 +17,14 @@ use tracing::debug; /// /// This is a wrapper around [`BestTransactions`] that also enforces a specific basefee. /// -/// This iterator guarantees that all transaction it returns satisfy the base fee. -pub(crate) struct BestTransactionsWithBasefee { +/// This iterator guarantees that all transaction it returns satisfy both the base fee and blob fee! +pub(crate) struct BestTransactionsWithFees { pub(crate) best: BestTransactions, pub(crate) base_fee: u64, + pub(crate) base_fee_per_blob_gas: u64, } -impl crate::traits::BestTransactions for BestTransactionsWithBasefee { +impl crate::traits::BestTransactions for BestTransactionsWithFees { fn mark_invalid(&mut self, tx: &Self::Item) { BestTransactions::mark_invalid(&mut self.best, tx) } @@ -41,7 +42,7 @@ impl crate::traits::BestTransactions for BestTransaction } } -impl Iterator for BestTransactionsWithBasefee { +impl Iterator for BestTransactionsWithFees { type Item = Arc>; fn next(&mut self) -> Option { @@ -52,6 +53,13 @@ impl Iterator for BestTransactionsWithBasefee { // tx violates base fee, mark it as invalid and continue crate::traits::BestTransactions::mark_invalid(self, &best); } else { + // tx is EIP4844 and violates blob fee, mark it as invalid and continue + if best.transaction.max_fee_per_blob_gas().is_some_and(|max_fee_per_blob_gas| { + max_fee_per_blob_gas < self.base_fee_per_blob_gas as u128 + }) { + crate::traits::BestTransactions::mark_invalid(self, &best); + continue; + }; return Some(best) } } diff --git a/crates/transaction-pool/src/pool/blob.rs b/crates/transaction-pool/src/pool/blob.rs index 6d2fa6822a..f19a45fc6b 100644 --- a/crates/transaction-pool/src/pool/blob.rs +++ b/crates/transaction-pool/src/pool/blob.rs @@ -80,12 +80,42 @@ impl BlobTransactions { Some(tx.transaction) } - /// Returns all transactions that satisfy the given basefee and blob_fee. - pub(crate) const fn satisfy_attributes( + /// Returns all transactions that satisfy the given basefee and blobfee. + /// + /// Note: This does not remove any the transactions from the pool. + pub(crate) fn satisfy_attributes( &self, - _best_transactions_attributes: BestTransactionsAttributes, + best_transactions_attributes: BestTransactionsAttributes, ) -> Vec>> { - Vec::new() + let mut transactions = Vec::new(); + { + // short path if blob_fee is None in provided best transactions attributes + if let Some(blob_fee_to_satisfy) = + best_transactions_attributes.blob_fee.map(|fee| fee as u128) + { + let mut iter = self.by_id.iter().peekable(); + + while let Some((id, tx)) = iter.next() { + if tx.transaction.max_fee_per_blob_gas().unwrap_or_default() < + blob_fee_to_satisfy || + tx.transaction.max_fee_per_gas() < + best_transactions_attributes.basefee as u128 + { + // does not satisfy the blob fee or base fee + // still parked in blob pool -> skip descendant transactions + 'this: while let Some((peek, _)) = iter.peek() { + if peek.sender != id.sender { + break 'this + } + iter.next(); + } + } else { + transactions.push(tx.transaction.clone()); + } + } + } + } + transactions } /// Returns true if the pool exceeds the given limit diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index 7166a70a43..9476f3c6cb 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -1,7 +1,7 @@ use crate::{ identifier::{SenderId, TransactionId}, pool::{ - best::{BestTransactions, BestTransactionsWithBasefee}, + best::{BestTransactions, BestTransactionsWithFees}, size::SizeTracker, }, Priority, SubPoolLimit, TransactionOrdering, ValidPoolTransaction, @@ -115,9 +115,13 @@ impl PendingPool { } } - /// Same as `best` but only returns transactions that satisfy the given basefee. - pub(crate) fn best_with_basefee(&self, base_fee: u64) -> BestTransactionsWithBasefee { - BestTransactionsWithBasefee { best: self.best(), base_fee } + /// Same as `best` but only returns transactions that satisfy the given basefee and blobfee. + pub(crate) fn best_with_basefee_and_blobfee( + &self, + base_fee: u64, + base_fee_per_blob_gas: u64, + ) -> BestTransactionsWithFees { + BestTransactionsWithFees { best: self.best(), base_fee, base_fee_per_blob_gas } } /// Same as `best` but also includes the given unlocked transactions. diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 7a81a8cd8f..46c105296f 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -287,38 +287,67 @@ impl TxPool { } } - /// Returns an iterator that yields transactions that are ready to be included in the block. + /// Returns an iterator that yields transactions that are ready to be included in the block with + /// the tracked fees. pub(crate) fn best_transactions(&self) -> BestTransactions { self.pending_pool.best() } /// Returns an iterator that yields transactions that are ready to be included in the block with /// the given base fee and optional blob fee. + /// + /// If the provided attributes differ from the currently tracked fees, this will also include + /// transactions that are unlocked by the new fees, or exclude transactions that are no longer + /// valid with the new fees. pub(crate) fn best_transactions_with_attributes( &self, best_transactions_attributes: BestTransactionsAttributes, ) -> Box>>> { + // First we need to check if the given base fee is different than what's currently being + // tracked match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee) { Ordering::Equal => { - // fee unchanged, nothing to shift - Box::new(self.best_transactions()) + // for EIP-4844 transactions we also need to check if the blob fee is now lower than + // what's currently being tracked, if so we need to include transactions from the + // blob pool that are valid with the lower blob fee + if best_transactions_attributes + .blob_fee + .map_or(false, |fee| fee < self.all_transactions.pending_fees.blob_fee as u64) + { + let unlocked_by_blob_fee = + self.blob_pool.satisfy_attributes(best_transactions_attributes); + + Box::new(self.pending_pool.best_with_unlocked( + unlocked_by_blob_fee, + self.all_transactions.pending_fees.base_fee, + )) + } else { + Box::new(self.pending_pool.best()) + } } Ordering::Greater => { // base fee increased, we only need to enforce this on the pending pool - Box::new(self.pending_pool.best_with_basefee(best_transactions_attributes.basefee)) + Box::new(self.pending_pool.best_with_basefee_and_blobfee( + best_transactions_attributes.basefee, + best_transactions_attributes.blob_fee.unwrap_or_default(), + )) } Ordering::Less => { - // base fee decreased, we need to move transactions from the basefee pool to the - // pending pool and satisfy blob fee transactions as well - let unlocked_with_blob = - self.blob_pool.satisfy_attributes(best_transactions_attributes); + // base fee decreased, we need to move transactions from the basefee + blob pool to + // the pending pool that might be unlocked by the lower base fee + let mut unlocked = self + .basefee_pool + .satisfy_base_fee_transactions(best_transactions_attributes.basefee); - Box::new(self.pending_pool.best_with_unlocked( - unlocked_with_blob, - self.all_transactions.pending_fees.base_fee, - )) + // also include blob pool transactions that are now unlocked + unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes)); + + Box::new( + self.pending_pool + .best_with_unlocked(unlocked, self.all_transactions.pending_fees.base_fee), + ) } } }