Skip to content

Commit

Permalink
test(txpool): add replacement test (#4596)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse authored Sep 14, 2023
1 parent 3d564aa commit 723036b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 2 deletions.
18 changes: 18 additions & 0 deletions crates/transaction-pool/src/pool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,24 @@ impl<T: PoolTransaction> AddedTransaction<T> {
}
}
}

/// Returns the subpool this transaction was added to
#[cfg(test)]
pub(crate) fn subpool(&self) -> SubPool {
match self {
AddedTransaction::Pending(_) => SubPool::Pending,
AddedTransaction::Parked { subpool, .. } => *subpool,
}
}

/// Returns the [TransactionId] of the added transaction
#[cfg(test)]
pub(crate) fn id(&self) -> &TransactionId {
match self {
AddedTransaction::Pending(added) => added.transaction.id(),
AddedTransaction::Parked { transaction, .. } => transaction.id(),
}
}
}

/// Contains all state changes after a [`CanonicalStateUpdate`] was processed
Expand Down
6 changes: 6 additions & 0 deletions crates/transaction-pool/src/pool/parked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ impl<T: ParkedOrd> ParkedPool<T> {
pub(crate) fn is_empty(&self) -> bool {
self.by_id.is_empty()
}

/// Returns `true` if the transaction with the given id is already included in this pool.
#[cfg(test)]
pub(crate) fn contains(&self, id: &TransactionId) -> bool {
self.by_id.contains_key(id)
}
}

impl<T: PoolTransaction> ParkedPool<BasefeeOrd<T>> {
Expand Down
6 changes: 6 additions & 0 deletions crates/transaction-pool/src/pool/pending.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ impl<T: TransactionOrdering> PendingPool<T> {
pub(crate) fn is_empty(&self) -> bool {
self.by_id.is_empty()
}

/// Returns `true` if the transaction with the given id is already included in this pool.
#[cfg(test)]
pub(crate) fn contains(&self, id: &TransactionId) -> bool {
self.by_id.contains_key(id)
}
}

/// A transaction that is ready to be included in a block.
Expand Down
51 changes: 49 additions & 2 deletions crates/transaction-pool/src/pool/txpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,16 @@ impl<T: TransactionOrdering> TxPool<T> {
self.all_transactions.contains(tx_hash)
}

/// Returns `true` if the transaction with the given id is already included in the given subpool
#[cfg(test)]
pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
match subpool {
SubPool::Queued => self.queued_pool.contains(id),
SubPool::Pending => self.pending_pool.contains(id),
SubPool::BaseFee => self.basefee_pool.contains(id),
}
}

/// Returns the transaction for the given hash.
pub(crate) fn get(
&self,
Expand Down Expand Up @@ -376,13 +386,15 @@ impl<T: TransactionOrdering> TxPool<T> {

match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
Ok(InsertOk { transaction, move_to, replaced_tx, updates, .. }) => {
// replace the new tx and remove the replaced in the subpool(s)
self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
// Update inserted transactions metric
self.metrics.inserted_transactions.increment(1);
let UpdateOutcome { promoted, discarded } = self.process_updates(updates);

// This transaction was moved to the pending pool.
let replaced = replaced_tx.map(|(tx, _)| tx);

// This transaction was moved to the pending pool.
let res = if move_to.is_pending() {
AddedTransaction::Pending(AddedPendingTransaction {
transaction,
Expand Down Expand Up @@ -678,6 +690,14 @@ impl<T: TransactionOrdering> TxPool<T> {
}
}

#[cfg(any(test, feature = "test-utils"))]
impl TxPool<crate::test_utils::MockOrdering> {
/// Creates a mock instance for testing.
pub fn mock() -> Self {
Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
}
}

// Additional test impls
#[cfg(any(test, feature = "test-utils"))]
#[allow(missing_docs)]
Expand Down Expand Up @@ -1698,19 +1718,46 @@ mod tests {
let mut pool = AllTransactions::default();
let tx = MockTransaction::eip1559().inc_price().inc_limit();
let first = f.validated(tx.clone());
let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
let replacement = f.validated(tx.rng_hash().inc_price());
let InsertOk { updates, replaced_tx, .. } =
pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
assert!(updates.is_empty());
let replaced = replaced_tx.unwrap();
assert_eq!(replaced.0.hash(), first.hash());

// ensure replaced tx is fully removed
assert!(!pool.contains(first.hash()));
assert!(pool.contains(replacement.hash()));
assert_eq!(pool.len(), 1);
}

#[test]
fn insert_replace_txpool() {
let on_chain_balance = U256::ZERO;
let on_chain_nonce = 0;
let mut f = MockTransactionFactory::default();
let mut pool = TxPool::mock();

let tx = MockTransaction::eip1559().inc_price().inc_limit();
let first = f.validated(tx.clone());
let first_added =
pool.add_transaction(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
let replacement = f.validated(tx.rng_hash().inc_price());
let replacement_added =
pool.add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();

// // ensure replaced tx removed
assert!(!pool.contains(first_added.hash()));
// but the replacement is still there
assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));

assert!(pool.contains(replacement.hash()));
let size = pool.size();
assert_eq!(size.total, 1);
size.assert_invariants();
}

#[test]
fn insert_replace_underpriced() {
let on_chain_balance = U256::ZERO;
Expand Down
10 changes: 10 additions & 0 deletions crates/transaction-pool/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,16 @@ pub struct PoolSize {
pub total: usize,
}

// === impl PoolSize ===

impl PoolSize {
/// Asserts that the invariants of the pool size are met.
#[cfg(test)]
pub(crate) fn assert_invariants(&self) {
assert_eq!(self.total, self.pending + self.basefee + self.queued);
}
}

/// Represents the current status of the pool.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct BlockInfo {
Expand Down

0 comments on commit 723036b

Please sign in to comment.