Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/node/core/src/args/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ impl RethTransactionPoolConfig for TxPoolArgs {
new_tx_listener_buffer_size: self.new_tx_listener_buffer_size,
max_new_pending_txs_notifications: self.max_new_pending_txs_notifications,
max_queued_lifetime: self.max_queued_lifetime,
..Default::default()
}
}

Expand Down
17 changes: 17 additions & 0 deletions crates/transaction-pool/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub const REPLACE_BLOB_PRICE_BUMP: u128 = 100;
/// Default maximum new transactions for broadcasting.
pub const MAX_NEW_PENDING_TXS_NOTIFICATIONS: usize = 200;

/// Default maximum allowed in flight delegated transactions per account.
pub const DEFAULT_MAX_INFLIGHT_DELEGATED_SLOTS: usize = 1;

/// Configuration options for the Transaction pool.
#[derive(Debug, Clone)]
pub struct PoolConfig {
Expand Down Expand Up @@ -65,6 +68,10 @@ pub struct PoolConfig {
pub max_new_pending_txs_notifications: usize,
/// Maximum lifetime for transactions in the pool
pub max_queued_lifetime: Duration,
/// The maximum allowed inflight transactions a delegated sender can have.
///
/// This restricts how many executable transaction a delegated sender can stack.
pub max_inflight_delegated_slot_limit: usize,
}

impl PoolConfig {
Expand All @@ -84,6 +91,15 @@ impl PoolConfig {
self
}

/// Configures how many slots are available for a delegated sender.
pub const fn with_max_inflight_delegated_slots(
mut self,
max_inflight_delegation_limit: usize,
) -> Self {
self.max_inflight_delegated_slot_limit = max_inflight_delegation_limit;
self
}

/// Returns whether the size and amount constraints in any sub-pools are exceeded.
#[inline]
pub const fn is_exceeded(&self, pool_size: PoolSize) -> bool {
Expand Down Expand Up @@ -112,6 +128,7 @@ impl Default for PoolConfig {
new_tx_listener_buffer_size: NEW_TX_LISTENER_BUFFER_SIZE,
max_new_pending_txs_notifications: MAX_NEW_PENDING_TXS_NOTIFICATIONS,
max_queued_lifetime: MAX_QUEUED_TRANSACTION_LIFETIME,
max_inflight_delegated_slot_limit: DEFAULT_MAX_INFLIGHT_DELEGATED_SLOTS,
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/transaction-pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,8 @@ pub use crate::{
batcher::{BatchTxProcessor, BatchTxRequest},
blobstore::{BlobStore, BlobStoreError},
config::{
LocalTransactionConfig, PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP,
LocalTransactionConfig, PoolConfig, PriceBumpConfig, SubPoolLimit,
DEFAULT_MAX_INFLIGHT_DELEGATED_SLOTS, DEFAULT_PRICE_BUMP,
DEFAULT_TXPOOL_ADDITIONAL_VALIDATION_TASKS, MAX_NEW_PENDING_TXS_NOTIFICATIONS,
REPLACE_BLOB_PRICE_BUMP, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
Expand Down
26 changes: 18 additions & 8 deletions crates/transaction-pool/src/pool/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,8 +771,8 @@ impl<T: TransactionOrdering> TxPool<T> {
}

/// Determines if the tx sender is delegated or has a pending delegation, and if so, ensures
/// they have at most one in-flight **executable** transaction, e.g. disallow stacked and
/// nonce-gapped transactions from the account.
/// they have at most one configured amount of in-flight **executable** transactions (default at
/// most one), e.g. disallow stacked and nonce-gapped transactions from the account.
fn check_delegation_limit(
&self,
transaction: &ValidPoolTransaction<T::Transaction>,
Expand Down Expand Up @@ -802,8 +802,17 @@ impl<T: TransactionOrdering> TxPool<T> {
return Ok(())
}

if txs_by_sender.any(|id| id == &transaction.transaction_id) {
// Transaction replacement is supported
let mut count = 0;
for id in txs_by_sender {
if id == &transaction.transaction_id {
// Transaction replacement is supported
return Ok(())
}
count += 1;
}

if count < self.config.max_inflight_delegated_slot_limit {
// account still has an available slot
return Ok(())
}

Expand All @@ -818,8 +827,9 @@ impl<T: TransactionOrdering> TxPool<T> {
/// This verifies that the transaction complies with code authorization
/// restrictions brought by EIP-7702 transaction type:
/// 1. Any account with a deployed delegation or an in-flight authorization to deploy a
/// delegation will only be allowed a single transaction slot instead of the standard limit.
/// This is due to the possibility of the account being sweeped by an unrelated account.
/// delegation will only be allowed a certain amount of transaction slots (default 1) instead
/// of the standard limit. This is due to the possibility of the account being sweeped by an
/// unrelated account.
/// 2. In case the pool is tracking a pending / queued transaction from a specific account, at
/// most one in-flight transaction is allowed; any additional delegated transactions from
/// that account will be rejected.
Expand All @@ -829,12 +839,12 @@ impl<T: TransactionOrdering> TxPool<T> {
on_chain_nonce: u64,
on_chain_code_hash: Option<B256>,
) -> Result<(), PoolError> {
// Allow at most one in-flight tx for delegated accounts or those with a
// pending authorization.
// Ensure in-flight limit for delegated accounts or those with a pending authorization.
self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;

if let Some(authority_list) = &transaction.authority_ids {
for sender_id in authority_list {
// Ensure authority has at most 1 inflight transaction.
if self.all_transactions.txs_iter(*sender_id).nth(1).is_some() {
return Err(PoolError::new(
*transaction.hash(),
Expand Down