mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
feat: add blob subpool variants and state (#4824)
This commit is contained in:
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user