feat: add BestTransactionsWithAttributes blob fee checks (#6674)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Nil Medvedev
2024-02-22 12:56:30 +01:00
committed by GitHub
parent d7a2123181
commit a272bbb76b
4 changed files with 95 additions and 24 deletions

View File

@ -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<T: TransactionOrdering> {
/// This iterator guarantees that all transaction it returns satisfy both the base fee and blob fee!
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 BestTransactionsWithBasefee<T> {
impl<T: TransactionOrdering> crate::traits::BestTransactions for BestTransactionsWithFees<T> {
fn mark_invalid(&mut self, tx: &Self::Item) {
BestTransactions::mark_invalid(&mut self.best, tx)
}
@ -41,7 +42,7 @@ impl<T: TransactionOrdering> crate::traits::BestTransactions for BestTransaction
}
}
impl<T: TransactionOrdering> Iterator for BestTransactionsWithBasefee<T> {
impl<T: TransactionOrdering> Iterator for BestTransactionsWithFees<T> {
type Item = Arc<ValidPoolTransaction<T::Transaction>>;
fn next(&mut self) -> Option<Self::Item> {
@ -52,6 +53,13 @@ impl<T: TransactionOrdering> Iterator for BestTransactionsWithBasefee<T> {
// 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)
}
}

View File

@ -80,12 +80,42 @@ impl<T: PoolTransaction> BlobTransactions<T> {
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<Arc<ValidPoolTransaction<T>>> {
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

View File

@ -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<T: TransactionOrdering> PendingPool<T> {
}
}
/// Same as `best` but only returns transactions that satisfy the given basefee.
pub(crate) fn best_with_basefee(&self, base_fee: u64) -> BestTransactionsWithBasefee<T> {
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<T> {
BestTransactionsWithFees { best: self.best(), base_fee, base_fee_per_blob_gas }
}
/// Same as `best` but also includes the given unlocked transactions.

View File

@ -287,38 +287,67 @@ impl<T: TransactionOrdering> TxPool<T> {
}
}
/// 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<T> {
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<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
{
// 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),
)
}
}
}