diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index a5acd6edb..75465afac 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -463,6 +463,20 @@ where self.pool.remove_transactions(hashes) } + fn remove_transactions_and_descendants( + &self, + hashes: Vec, + ) -> Vec>> { + self.pool.remove_transactions_and_descendants(hashes) + } + + fn remove_transactions_by_sender( + &self, + sender: Address, + ) -> Vec>> { + self.pool.remove_transactions_by_sender(sender) + } + fn retain_unknown(&self, announcement: &mut A) where A: HandleMempoolData, diff --git a/crates/transaction-pool/src/noop.rs b/crates/transaction-pool/src/noop.rs index ddab4f622..681aa9896 100644 --- a/crates/transaction-pool/src/noop.rs +++ b/crates/transaction-pool/src/noop.rs @@ -183,6 +183,20 @@ impl TransactionPool for NoopTransactionPool { vec![] } + fn remove_transactions_and_descendants( + &self, + _hashes: Vec, + ) -> Vec>> { + vec![] + } + + fn remove_transactions_by_sender( + &self, + _sender: Address, + ) -> Vec>> { + vec![] + } + fn retain_unknown(&self, _announcement: &mut A) where A: HandleMempoolData, diff --git a/crates/transaction-pool/src/pool/mod.rs b/crates/transaction-pool/src/pool/mod.rs index 090b92fb6..44dd5a8d5 100644 --- a/crates/transaction-pool/src/pool/mod.rs +++ b/crates/transaction-pool/src/pool/mod.rs @@ -195,7 +195,7 @@ where pub(crate) fn block_info(&self) -> BlockInfo { self.get_pool_data().block_info() } - /// Returns the currently tracked block + /// Sets the currently tracked block pub(crate) fn set_block_info(&self, info: BlockInfo) { self.pool.write().set_block_info(info) } @@ -715,6 +715,38 @@ where removed } + /// Removes and returns all matching transactions and their dependent transactions from the + /// pool. + pub(crate) fn remove_transactions_and_descendants( + &self, + hashes: Vec, + ) -> Vec>> { + if hashes.is_empty() { + return Vec::new() + } + let removed = self.pool.write().remove_transactions_and_descendants(hashes); + + let mut listener = self.event_listener.write(); + + removed.iter().for_each(|tx| listener.discarded(tx.hash())); + + removed + } + + pub(crate) fn remove_transactions_by_sender( + &self, + sender: Address, + ) -> Vec>> { + let sender_id = self.get_sender_id(sender); + let removed = self.pool.write().remove_transactions_by_sender(sender_id); + + let mut listener = self.event_listener.write(); + + removed.iter().for_each(|tx| listener.discarded(tx.hash())); + + removed + } + /// Removes and returns all transactions that are present in the pool. pub(crate) fn retain_unknown(&self, announcement: &mut A) where diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 10605565c..7aaa77df4 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -663,6 +663,38 @@ impl TxPool { txs } + /// Removes and returns all matching transactions and their descendants from the pool. + pub(crate) fn remove_transactions_and_descendants( + &mut self, + hashes: Vec, + ) -> Vec>> { + let mut removed = Vec::new(); + for hash in hashes { + if let Some(tx) = self.remove_transaction_by_hash(&hash) { + removed.push(tx.clone()); + self.remove_descendants(tx.id(), &mut removed); + } + } + self.update_size_metrics(); + removed + } + + /// Removes all transactions from the given sender. + pub(crate) fn remove_transactions_by_sender( + &mut self, + sender_id: SenderId, + ) -> Vec>> { + let mut removed = Vec::new(); + let txs = self.get_transactions_by_sender(sender_id); + for tx in txs { + if let Some(tx) = self.remove_transaction(tx.id()) { + removed.push(tx); + } + } + self.update_size_metrics(); + removed + } + /// Remove the transaction from the __entire__ pool. /// /// This includes the total set of transaction and the subpool it currently resides in. @@ -2963,6 +2995,148 @@ mod tests { assert_eq!(vec![v1.nonce()], pool_txs); } #[test] + fn test_remove_transactions() { + let on_chain_balance = U256::from(10_000); + let on_chain_nonce = 0; + let mut f = MockTransactionFactory::default(); + let mut pool = TxPool::new(MockOrdering::default(), Default::default()); + + let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit(); + let tx_1 = tx_0.next(); + let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit(); + let tx_3 = tx_2.next(); + + // Create 4 transactions + let v0 = f.validated(tx_0); + let v1 = f.validated(tx_1); + let v2 = f.validated(tx_2); + let v3 = f.validated(tx_3); + + // Add them to the pool + let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce).unwrap(); + + assert_eq!(0, pool.queued_transactions().len()); + assert_eq!(4, pool.pending_transactions().len()); + + pool.remove_transactions(vec![*v0.hash(), *v2.hash()]); + + assert_eq!(0, pool.queued_transactions().len()); + assert_eq!(2, pool.pending_transactions().len()); + assert!(pool.contains(v1.hash())); + assert!(pool.contains(v3.hash())); + } + + #[test] + fn test_remove_transactions_and_descendants() { + let on_chain_balance = U256::from(10_000); + let on_chain_nonce = 0; + let mut f = MockTransactionFactory::default(); + let mut pool = TxPool::new(MockOrdering::default(), Default::default()); + + let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit(); + let tx_1 = tx_0.next(); + let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit(); + let tx_3 = tx_2.next(); + let tx_4 = tx_3.next(); + + // Create 5 transactions + let v0 = f.validated(tx_0); + let v1 = f.validated(tx_1); + let v2 = f.validated(tx_2); + let v3 = f.validated(tx_3); + let v4 = f.validated(tx_4); + + // Add them to the pool + let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce).unwrap(); + + assert_eq!(0, pool.queued_transactions().len()); + assert_eq!(5, pool.pending_transactions().len()); + + pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]); + + assert_eq!(0, pool.queued_transactions().len()); + assert_eq!(0, pool.pending_transactions().len()); + } + #[test] + fn test_remove_descendants() { + let on_chain_balance = U256::from(10_000); + let on_chain_nonce = 0; + let mut f = MockTransactionFactory::default(); + let mut pool = TxPool::new(MockOrdering::default(), Default::default()); + + let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit(); + let tx_1 = tx_0.next(); + let tx_2 = tx_1.next(); + let tx_3 = tx_2.next(); + + // Create 4 transactions + let v0 = f.validated(tx_0); + let v1 = f.validated(tx_1); + let v2 = f.validated(tx_2); + let v3 = f.validated(tx_3); + + // Add them to the pool + let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap(); + + assert_eq!(0, pool.queued_transactions().len()); + assert_eq!(4, pool.pending_transactions().len()); + + let mut removed = Vec::new(); + pool.remove_transaction(v0.id()); + pool.remove_descendants(v0.id(), &mut removed); + + assert_eq!(0, pool.queued_transactions().len()); + assert_eq!(0, pool.pending_transactions().len()); + assert_eq!(3, removed.len()); + } + #[test] + fn test_remove_transactions_by_sender() { + let on_chain_balance = U256::from(10_000); + let on_chain_nonce = 0; + let mut f = MockTransactionFactory::default(); + let mut pool = TxPool::new(MockOrdering::default(), Default::default()); + + let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit(); + let tx_1 = tx_0.next(); + let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit(); + let tx_3 = tx_2.next(); + let tx_4 = tx_3.next(); + + // Create 5 transactions + let v0 = f.validated(tx_0); + let v1 = f.validated(tx_1); + let v2 = f.validated(tx_2); + let v3 = f.validated(tx_3); + let v4 = f.validated(tx_4); + + // Add them to the pool + let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap(); + let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce).unwrap(); + + assert_eq!(0, pool.queued_transactions().len()); + assert_eq!(5, pool.pending_transactions().len()); + + pool.remove_transactions_by_sender(v2.sender_id()); + + assert_eq!(0, pool.queued_transactions().len()); + assert_eq!(2, pool.pending_transactions().len()); + assert!(pool.contains(v0.hash())); + assert!(pool.contains(v1.hash())); + } + #[test] fn wrong_best_order_of_transactions() { let on_chain_balance = U256::from(10_000); let mut on_chain_nonce = 0; diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index adae238e4..07fe9b9c2 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -293,14 +293,30 @@ pub trait TransactionPool: Send + Sync + Clone { /// Removes all transactions corresponding to the given hashes. /// - /// Also removes all _dependent_ transactions. - /// /// Consumer: Utility fn remove_transactions( &self, hashes: Vec, ) -> Vec>>; + /// Removes all transactions corresponding to the given hashes. + /// + /// Also removes all _dependent_ transactions. + /// + /// Consumer: Utility + fn remove_transactions_and_descendants( + &self, + hashes: Vec, + ) -> Vec>>; + + /// Removes all transactions from the given sender + /// + /// Consumer: Utility + fn remove_transactions_by_sender( + &self, + sender: Address, + ) -> Vec>>; + /// Retains only those hashes that are unknown to the pool. /// In other words, removes all transactions from the given set that are currently present in /// the pool. Returns hashes already known to the pool.