refactor(txpool): use tx hash for on new block update (#170)

* refactor(txpool): use tx hash for updates

* chore: rustfmt
This commit is contained in:
Matthias Seitz
2022-11-07 14:50:36 +01:00
committed by GitHub
parent b7cdfbf4f9
commit e870a4ce13
6 changed files with 107 additions and 24 deletions

View File

@ -78,12 +78,12 @@
pub use crate::{ pub use crate::{
config::PoolConfig, config::PoolConfig,
ordering::TransactionOrdering, ordering::TransactionOrdering,
traits::{BestTransactions, NewBlockEvent, PoolTransaction, TransactionPool}, traits::{BestTransactions, OnNewBlockEvent, PoolTransaction, TransactionPool},
validate::{TransactionValidationOutcome, TransactionValidator}, validate::{TransactionValidationOutcome, TransactionValidator},
}; };
use crate::{ use crate::{
error::PoolResult, error::PoolResult,
pool::PoolInner, pool::{OnNewBlockOutcome, PoolInner},
traits::{NewTransactionEvent, PoolStatus, TransactionOrigin}, traits::{NewTransactionEvent, PoolStatus, TransactionOrigin},
validate::ValidPoolTransaction, validate::ValidPoolTransaction,
}; };
@ -178,9 +178,8 @@ where
self.pool.status() self.pool.status()
} }
fn on_new_block(&self, _event: NewBlockEvent<Self::Transaction>) { fn on_new_block(&self, event: OnNewBlockEvent) {
// TODO perform maintenance: update pool accordingly self.pool.on_new_block(event);
todo!()
} }
async fn add_transaction( async fn add_transaction(

View File

@ -11,14 +11,13 @@ pub enum TransactionEvent<Hash> {
/// Transaction has been added to the queued pool. /// Transaction has been added to the queued pool.
Queued, Queued,
/// Transaction has been included in the block belonging to this hash. /// Transaction has been included in the block belonging to this hash.
Included(H256), Mined(H256),
/// Transaction has been replaced by the transaction belonging to the hash. /// Transaction has been replaced by the transaction belonging to the hash.
/// ///
/// E.g. same (sender + nonce) pair /// E.g. same (sender + nonce) pair
Replaced(Hash), Replaced(Hash),
/// Transaction was dropped due to configured limits. /// Transaction was dropped due to configured limits.
Dropped, Discarded,
/// Transaction became invalid indefinitely. /// Transaction became invalid indefinitely.
Invalid, Invalid,
// TODO Timedout?, broadcasted(peers)
} }

View File

@ -2,6 +2,7 @@
use crate::pool::events::TransactionEvent; use crate::pool::events::TransactionEvent;
use futures::channel::mpsc::UnboundedSender; use futures::channel::mpsc::UnboundedSender;
use reth_primitives::H256;
use std::{collections::HashMap, hash}; use std::{collections::HashMap, hash};
type EventSink<Hash> = UnboundedSender<TransactionEvent<Hash>>; type EventSink<Hash> = UnboundedSender<TransactionEvent<Hash>>;
@ -44,6 +45,16 @@ impl<Hash: hash::Hash + Eq + Clone> PoolEventListener<Hash> {
pub(crate) fn queued(&mut self, tx: &Hash) { pub(crate) fn queued(&mut self, tx: &Hash) {
self.notify_with(tx, |notifier| notifier.queued()); self.notify_with(tx, |notifier| notifier.queued());
} }
/// Notify listeners about a transaction that was discarded.
pub(crate) fn discarded(&mut self, tx: &Hash) {
self.notify_with(tx, |notifier| notifier.discarded());
}
/// Notify listeners that the transaction was mined
pub(crate) fn mined(&mut self, tx: &Hash, block_hash: H256) {
self.notify_with(tx, |notifier| notifier.mined(block_hash));
}
} }
impl<Hash: hash::Hash + Eq> Default for PoolEventListener<Hash> { impl<Hash: hash::Hash + Eq> Default for PoolEventListener<Hash> {
@ -86,4 +97,16 @@ impl<Hash: Clone> PoolEventNotifier<Hash> {
self.notify(TransactionEvent::Replaced(hash)); self.notify(TransactionEvent::Replaced(hash));
self.is_done = true; self.is_done = true;
} }
/// Transaction was mined.
fn mined(&mut self, block_hash: H256) {
self.notify(TransactionEvent::Mined(block_hash));
self.is_done = true;
}
/// Transaction was replaced with the given transaction
fn discarded(&mut self) {
self.notify(TransactionEvent::Discarded);
self.is_done = true;
}
} }

View File

@ -69,13 +69,13 @@ use crate::{
pool::{listener::PoolEventListener, state::SubPool, txpool::TxPool}, pool::{listener::PoolEventListener, state::SubPool, txpool::TxPool},
traits::{NewTransactionEvent, PoolStatus, PoolTransaction, TransactionOrigin}, traits::{NewTransactionEvent, PoolStatus, PoolTransaction, TransactionOrigin},
validate::{TransactionValidationOutcome, ValidPoolTransaction}, validate::{TransactionValidationOutcome, ValidPoolTransaction},
PoolConfig, TransactionOrdering, TransactionValidator, U256, OnNewBlockEvent, PoolConfig, TransactionOrdering, TransactionValidator, U256,
}; };
use best::BestTransactions; use best::BestTransactions;
pub use events::TransactionEvent; pub use events::TransactionEvent;
use futures::channel::mpsc::{channel, Receiver, Sender}; use futures::channel::mpsc::{channel, Receiver, Sender};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use reth_primitives::{Address, TxHash}; use reth_primitives::{Address, TxHash, H256};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
sync::Arc, sync::Arc,
@ -164,6 +164,12 @@ where
rx rx
} }
/// Updates the entire pool after a new block was mined.
pub(crate) fn on_new_block(&self, block: OnNewBlockEvent) {
let outcome = self.pool.write().on_new_block(block);
self.notify_on_new_block(outcome);
}
/// Resubmits transactions back into the pool. /// Resubmits transactions back into the pool.
pub fn resubmit(&self, _transactions: HashMap<TxHash, ValidPoolTransaction<T::Transaction>>) { pub fn resubmit(&self, _transactions: HashMap<TxHash, ValidPoolTransaction<T::Transaction>>) {
unimplemented!() unimplemented!()
@ -285,14 +291,28 @@ where
}); });
} }
/// Notifies transaction listeners about changes after a block was processed.
fn notify_on_new_block(&self, outcome: OnNewBlockOutcome) {
let OnNewBlockOutcome { mined, promoted, discarded, block_hash } = outcome;
let mut listener = self.event_listener.write();
mined.iter().for_each(|tx| listener.mined(tx, block_hash));
promoted.iter().for_each(|tx| listener.ready(tx, None));
discarded.iter().for_each(|tx| listener.discarded(tx));
}
/// Fire events for the newly added transaction. /// Fire events for the newly added transaction.
fn notify_event_listeners(&self, tx: &AddedTransaction<T::Transaction>) { fn notify_event_listeners(&self, tx: &AddedTransaction<T::Transaction>) {
let mut listener = self.event_listener.write(); let mut listener = self.event_listener.write();
match tx { match tx {
AddedTransaction::Pending(tx) => { AddedTransaction::Pending(tx) => {
listener.ready(tx.transaction.hash(), None); let AddedPendingTransaction { transaction, promoted, discarded, .. } = tx;
// TODO more listeners for discarded, removed etc...
listener.ready(transaction.hash(), None);
promoted.iter().for_each(|tx| listener.ready(tx, None));
discarded.iter().for_each(|tx| listener.discarded(tx));
} }
AddedTransaction::Parked { transaction, .. } => { AddedTransaction::Parked { transaction, .. } => {
listener.queued(transaction.hash()); listener.queued(transaction.hash());
@ -409,3 +429,16 @@ impl<T: PoolTransaction> AddedTransaction<T> {
} }
} }
} }
/// Contains all state changes after a [`NewBlockEvent`] was processed
#[derive(Debug)]
pub(crate) struct OnNewBlockOutcome {
/// Hash of the block.
pub(crate) block_hash: H256,
/// All mined transactions.
pub(crate) mined: Vec<TxHash>,
/// Transactions promoted to the ready queue.
pub(crate) promoted: Vec<TxHash>,
/// transaction that were discarded during the update
pub(crate) discarded: Vec<TxHash>,
}

View File

@ -9,14 +9,14 @@ use crate::{
pending::PendingPool, pending::PendingPool,
state::{SubPool, TxState}, state::{SubPool, TxState},
update::{Destination, PoolUpdate}, update::{Destination, PoolUpdate},
AddedPendingTransaction, AddedTransaction, AddedPendingTransaction, AddedTransaction, OnNewBlockOutcome,
}, },
traits::{PoolStatus, StateDiff}, traits::{PoolStatus, StateDiff},
NewBlockEvent, PoolConfig, PoolResult, PoolTransaction, TransactionOrdering, OnNewBlockEvent, PoolConfig, PoolResult, PoolTransaction, TransactionOrdering,
ValidPoolTransaction, U256, ValidPoolTransaction, U256,
}; };
use fnv::FnvHashMap; use fnv::FnvHashMap;
use reth_primitives::TxHash; use reth_primitives::{TxHash, H256};
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}, collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
@ -152,19 +152,25 @@ impl<T: TransactionOrdering> TxPool<T> {
/// ///
/// This removes all mined transactions, updates according to the new base fee and rechecks /// This removes all mined transactions, updates according to the new base fee and rechecks
/// sender allowance. /// sender allowance.
pub(crate) fn on_new_block(&mut self, block: NewBlockEvent<T::Transaction>) { pub(crate) fn on_new_block(&mut self, event: OnNewBlockEvent) -> OnNewBlockOutcome {
// Remove all transaction that were included in the block // Remove all transaction that were included in the block
for mined in &block.mined_transactions { for tx_hash in &event.mined_transactions {
self.all_transactions.remove_transaction(mined.id()); self.remove_transaction_by_hash(tx_hash);
self.pending_pool.remove_transaction(mined.id());
} }
// Apply the state changes to the total set of transactions which triggers sub-pool updates. // Apply the state changes to the total set of transactions which triggers sub-pool updates.
let updates = let updates =
self.all_transactions.update(block.pending_block_base_fee, &block.state_changes); self.all_transactions.update(event.pending_block_base_fee, &event.state_changes);
// Process the sub-pool updates // Process the sub-pool updates
self.process_updates(updates); let UpdateOutcome { promoted, discarded, .. } = self.process_updates(updates);
OnNewBlockOutcome {
block_hash: event.hash,
mined: event.mined_transactions,
promoted,
discarded,
}
} }
/// Adds the transaction into the pool. /// Adds the transaction into the pool.
@ -273,6 +279,17 @@ impl<T: TransactionOrdering> TxPool<T> {
self.remove_from_subpool(pool, tx.id()) self.remove_from_subpool(pool, tx.id())
} }
/// Remove the transaction from the entire pool via its hash.
///
/// This includes the total set of transaction and the subpool it currently resides in.
fn remove_transaction_by_hash(
&mut self,
tx_hash: &H256,
) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
self.remove_from_subpool(pool, tx.id())
}
/// Removes the transaction from the given pool. /// Removes the transaction from the given pool.
/// ///
/// Caution: this only removes the tx from the sub-pool and not from the pool itself /// Caution: this only removes the tx from the sub-pool and not from the pool itself
@ -675,6 +692,18 @@ impl<T: PoolTransaction> AllTransactions<T> {
self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender) self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
} }
/// Removes a transaction from the set using its hash.
pub(crate) fn remove_transaction_by_hash(
&mut self,
tx_hash: &H256,
) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
let tx = self.by_hash.remove(tx_hash)?;
let internal = self.txs.remove(&tx.transaction_id)?;
// decrement the counter for the sender.
self.tx_decr(tx.sender_id());
Some((tx, internal.subpool))
}
/// Removes a transaction from the set. /// Removes a transaction from the set.
/// ///
/// This will _not_ trigger additional updates, because descendants without nonce gaps are /// This will _not_ trigger additional updates, because descendants without nonce gaps are

View File

@ -21,7 +21,7 @@ pub trait TransactionPool: Send + Sync + 'static {
/// Implementers need to update the pool accordingly. /// Implementers need to update the pool accordingly.
/// For example the base fee of the pending block is determined after a block is mined which /// For example the base fee of the pending block is determined after a block is mined which
/// affects the dynamic fee requirement of pending transactions in the pool. /// affects the dynamic fee requirement of pending transactions in the pool.
fn on_new_block(&self, event: NewBlockEvent<Self::Transaction>); fn on_new_block(&self, event: OnNewBlockEvent);
/// Adds an _unvalidated_ transaction into the pool. /// Adds an _unvalidated_ transaction into the pool.
/// ///
@ -128,7 +128,7 @@ impl TransactionOrigin {
/// Event fired when a new block was mined /// Event fired when a new block was mined
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NewBlockEvent<T: PoolTransaction> { pub struct OnNewBlockEvent {
/// Hash of the added block. /// Hash of the added block.
pub hash: H256, pub hash: H256,
/// EIP-1559 Base fee of the _next_ (pending) block /// EIP-1559 Base fee of the _next_ (pending) block
@ -138,7 +138,7 @@ pub struct NewBlockEvent<T: PoolTransaction> {
/// Provides a set of state changes that affected the accounts. /// Provides a set of state changes that affected the accounts.
pub state_changes: StateDiff, pub state_changes: StateDiff,
/// All mined transactions in the block /// All mined transactions in the block
pub mined_transactions: Vec<Arc<ValidPoolTransaction<T>>>, pub mined_transactions: Vec<H256>,
} }
/// Contains a list of changed state /// Contains a list of changed state