feat: add blob subpool variants and state (#4824)

This commit is contained in:
Matthias Seitz
2023-09-27 15:45:14 +02:00
committed by GitHub
parent 5b2c03a483
commit ac6570fa7a
2 changed files with 55 additions and 11 deletions

View File

@ -5,31 +5,37 @@ bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub(crate) struct TxState: u8 { pub(crate) struct TxState: u8 {
/// Set to `1` if all ancestor transactions are pending. /// Set to `1` if all ancestor transactions are pending.
const NO_PARKED_ANCESTORS = 0b100000; const NO_PARKED_ANCESTORS = 0b10000000;
/// Set to `1` of the transaction is either the next transaction of the sender (on chain nonce == tx.nonce) or all prior transactions are also present in the pool. /// Set to `1` of the transaction is either the next transaction of the sender (on chain nonce == tx.nonce) or all prior transactions are also present in the pool.
const NO_NONCE_GAPS = 0b010000; const NO_NONCE_GAPS = 0b01000000;
/// Bit derived from the sender's balance. /// Bit derived from the sender's balance.
/// ///
/// Set to `1` if the sender's balance can cover the maximum cost for this transaction (`feeCap * gasLimit + value`). /// Set to `1` if the sender's balance can cover the maximum cost for this transaction (`feeCap * gasLimit + value`).
/// This includes cumulative costs of prior transactions, which ensures that the sender has enough funds for all max cost of prior transactions. /// This includes cumulative costs of prior transactions, which ensures that the sender has enough funds for all max cost of prior transactions.
const ENOUGH_BALANCE = 0b001000; const ENOUGH_BALANCE = 0b00100000;
/// Bit set to true if the transaction has a lower gas limit than the block's gas limit /// Bit set to true if the transaction has a lower gas limit than the block's gas limit
const NOT_TOO_MUCH_GAS = 0b000100; const NOT_TOO_MUCH_GAS = 0b00010000;
/// Covers the Dynamic fee requirement. /// Covers the Dynamic fee requirement.
/// ///
/// Set to 1 if `maxBlobFeePerGas` of the transaction meets the requirement of the pending block. /// Set to 1 if `maxBlobFeePerGas` of the transaction meets the requirement of the pending block.
const ENOUGH_FEE_CAP_BLOCK = 0b000010; const ENOUGH_FEE_CAP_BLOCK = 0b00001000;
/// Covers the dynamic blob fee requirement, only relevant for EIP-4844 blob transactions /// Covers the dynamic blob fee requirement, only relevant for EIP-4844 blob transactions
/// ///
/// Set to 1 if `maxBlobFeePer` of the transaction meets the requirement of the pending block. /// Set to 1 if `maxBlobFeePer` of the transaction meets the requirement of the pending block.
const ENOUGH_BLOB_FEE_CAP_BLOCK = 0b000001; const ENOUGH_BLOB_FEE_CAP_BLOCK = 0b00000100;
const PENDING_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits()| Self::NO_NONCE_GAPS.bits() | Self::ENOUGH_BALANCE.bits() | Self::NOT_TOO_MUCH_GAS.bits() | Self::ENOUGH_FEE_CAP_BLOCK.bits() | Self::ENOUGH_BLOB_FEE_CAP_BLOCK.bits(); /// Marks whether the transaction is a blob transaction
///
/// We track this as part of the state for simplicity, since blob transactions are handled differently and are mutually exclusive with normal transactions.
const BLOB_TRANSACTION = 0b00000010;
const PENDING_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits() | Self::NO_NONCE_GAPS.bits() | Self::ENOUGH_BALANCE.bits() | Self::NOT_TOO_MUCH_GAS.bits() | Self::ENOUGH_FEE_CAP_BLOCK.bits() | Self::ENOUGH_BLOB_FEE_CAP_BLOCK.bits();
const BASE_FEE_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits() | Self::NO_NONCE_GAPS.bits() | Self::ENOUGH_BALANCE.bits() | Self::NOT_TOO_MUCH_GAS.bits(); const BASE_FEE_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits() | Self::NO_NONCE_GAPS.bits() | Self::ENOUGH_BALANCE.bits() | Self::NOT_TOO_MUCH_GAS.bits();
const QUEUED_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits(); const QUEUED_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits();
const BLOB_POOL_BITS = Self::BLOB_TRANSACTION.bits();
} }
} }
@ -40,11 +46,18 @@ impl TxState {
/// - _No_ parked ancestors /// - _No_ parked ancestors
/// - enough balance /// - enough balance
/// - enough fee cap /// - enough fee cap
/// - enough blob fee cap
#[inline] #[inline]
pub(crate) fn is_pending(&self) -> bool { pub(crate) fn is_pending(&self) -> bool {
self.bits() >= TxState::PENDING_POOL_BITS.bits() self.bits() >= TxState::PENDING_POOL_BITS.bits()
} }
/// Whether this transaction is a blob transaction.
#[inline]
pub(crate) fn is_blob(&self) -> bool {
self.contains(TxState::BLOB_TRANSACTION)
}
/// Returns `true` if the transaction has a nonce gap. /// Returns `true` if the transaction has a nonce gap.
#[inline] #[inline]
pub(crate) fn has_nonce_gap(&self) -> bool { pub(crate) fn has_nonce_gap(&self) -> bool {
@ -62,6 +75,8 @@ pub enum SubPool {
/// The base-fee sub-pool contains transactions that are not ready to be included in the next /// The base-fee sub-pool contains transactions that are not ready to be included in the next
/// block because they don't meet the base fee requirement. /// block because they don't meet the base fee requirement.
BaseFee, BaseFee,
/// The blob sub-pool contains all blob transactions that are __not__ pending.
Blob,
/// The pending sub-pool contains transactions that are ready to be included in the next block. /// The pending sub-pool contains transactions that are ready to be included in the next block.
Pending, Pending,
} }
@ -87,6 +102,12 @@ impl SubPool {
matches!(self, SubPool::BaseFee) matches!(self, SubPool::BaseFee)
} }
/// Whether this transaction is in the blob pool.
#[inline]
pub fn is_blob(&self) -> bool {
matches!(self, SubPool::Blob)
}
/// Returns whether this is a promotion depending on the current sub-pool location. /// Returns whether this is a promotion depending on the current sub-pool location.
#[inline] #[inline]
pub fn is_promoted(&self, other: SubPool) -> bool { pub fn is_promoted(&self, other: SubPool) -> bool {
@ -99,6 +120,10 @@ impl From<TxState> for SubPool {
if value.is_pending() { if value.is_pending() {
return SubPool::Pending return SubPool::Pending
} }
if value.is_blob() {
// all _non-pending_ blob transactions are in the blob sub-pool
return SubPool::Blob
}
if value.bits() < TxState::BASE_FEE_POOL_BITS.bits() { if value.bits() < TxState::BASE_FEE_POOL_BITS.bits() {
return SubPool::Queued return SubPool::Queued
} }
@ -115,6 +140,7 @@ mod tests {
assert!(SubPool::BaseFee.is_promoted(SubPool::Queued)); assert!(SubPool::BaseFee.is_promoted(SubPool::Queued));
assert!(SubPool::Pending.is_promoted(SubPool::BaseFee)); assert!(SubPool::Pending.is_promoted(SubPool::BaseFee));
assert!(SubPool::Pending.is_promoted(SubPool::Queued)); assert!(SubPool::Pending.is_promoted(SubPool::Queued));
assert!(SubPool::Pending.is_promoted(SubPool::Blob));
assert!(!SubPool::BaseFee.is_promoted(SubPool::Pending)); assert!(!SubPool::BaseFee.is_promoted(SubPool::Pending));
assert!(!SubPool::Queued.is_promoted(SubPool::BaseFee)); assert!(!SubPool::Queued.is_promoted(SubPool::BaseFee));
} }
@ -144,14 +170,25 @@ mod tests {
assert_eq!(SubPool::Pending, state.into()); assert_eq!(SubPool::Pending, state.into());
assert!(state.is_pending()); assert!(state.is_pending());
let bits = 0b111111; let bits = 0b11111100;
let state = TxState::from_bits(bits).unwrap(); let state = TxState::from_bits(bits).unwrap();
assert_eq!(SubPool::Pending, state.into()); assert_eq!(SubPool::Pending, state.into());
assert!(state.is_pending()); assert!(state.is_pending());
let bits = 0b111111; let bits = 0b11111110;
let state = TxState::from_bits(bits).unwrap(); let state = TxState::from_bits(bits).unwrap();
assert_eq!(SubPool::Pending, state.into()); assert_eq!(SubPool::Pending, state.into());
assert!(state.is_pending()); assert!(state.is_pending());
} }
#[test]
fn test_blob() {
let mut state = TxState::PENDING_POOL_BITS;
state.insert(TxState::BLOB_TRANSACTION);
assert!(state.is_pending());
state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
assert!(state.is_blob());
assert!(!state.is_pending());
}
} }

View File

@ -88,7 +88,6 @@ pub struct TxPool<T: TransactionOrdering> {
/// be moved to pending if the base fee changes in their favor (decreases) in future blocks. /// be moved to pending if the base fee changes in their favor (decreases) in future blocks.
basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>, basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
/// All blob transactions in the pool /// All blob transactions in the pool
#[allow(unused)]
blob_transactions: BlobTransactions<T::Transaction>, blob_transactions: BlobTransactions<T::Transaction>,
/// All transactions in the pool. /// All transactions in the pool.
all_transactions: AllTransactions<T::Transaction>, all_transactions: AllTransactions<T::Transaction>,
@ -269,6 +268,7 @@ impl<T: TransactionOrdering> TxPool<T> {
SubPool::Queued => self.queued_pool.contains(id), SubPool::Queued => self.queued_pool.contains(id),
SubPool::Pending => self.pending_pool.contains(id), SubPool::Pending => self.pending_pool.contains(id),
SubPool::BaseFee => self.basefee_pool.contains(id), SubPool::BaseFee => self.basefee_pool.contains(id),
SubPool::Blob => self.blob_transactions.contains(id),
} }
} }
@ -561,6 +561,7 @@ impl<T: TransactionOrdering> TxPool<T> {
SubPool::Queued => self.queued_pool.remove_transaction(tx), SubPool::Queued => self.queued_pool.remove_transaction(tx),
SubPool::Pending => self.pending_pool.remove_transaction(tx), SubPool::Pending => self.pending_pool.remove_transaction(tx),
SubPool::BaseFee => self.basefee_pool.remove_transaction(tx), SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
SubPool::Blob => self.blob_transactions.remove_transaction(tx),
} }
} }
@ -572,9 +573,10 @@ impl<T: TransactionOrdering> TxPool<T> {
tx: &TransactionId, tx: &TransactionId,
) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> { ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
match pool { match pool {
SubPool::Queued => self.queued_pool.remove_transaction(tx),
SubPool::Pending => self.pending_pool.prune_transaction(tx), SubPool::Pending => self.pending_pool.prune_transaction(tx),
SubPool::Queued => self.queued_pool.remove_transaction(tx),
SubPool::BaseFee => self.basefee_pool.remove_transaction(tx), SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
SubPool::Blob => self.blob_transactions.remove_transaction(tx),
} }
} }
@ -619,6 +621,9 @@ impl<T: TransactionOrdering> TxPool<T> {
SubPool::BaseFee => { SubPool::BaseFee => {
self.basefee_pool.add_transaction(tx); self.basefee_pool.add_transaction(tx);
} }
SubPool::Blob => {
self.blob_transactions.add_transaction(tx);
}
} }
} }
@ -1324,6 +1329,8 @@ impl<T: PoolTransaction> AllTransactions<T> {
// before attempting to insert a blob transaction, we need to ensure that additional // before attempting to insert a blob transaction, we need to ensure that additional
// constraints are met that only apply to blob transactions // constraints are met that only apply to blob transactions
if transaction.is_eip4844() { if transaction.is_eip4844() {
state.insert(TxState::BLOB_TRANSACTION);
transaction = transaction =
self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?; self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default(); let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();