feat(txpool): function to return the next free nonce (#11744)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Kien Trinh
2024-10-16 15:05:56 +07:00
committed by GitHub
parent 3f3a7ef023
commit b8147708ad
6 changed files with 98 additions and 0 deletions

View File

@ -59,6 +59,11 @@ impl SenderId {
pub const fn start_bound(self) -> std::ops::Bound<TransactionId> {
std::ops::Bound::Included(TransactionId::new(self, 0))
}
/// Converts the sender to a [`TransactionId`] with the given nonce.
pub const fn into_id(self, nonce: u64) -> TransactionId {
TransactionId::new(self, nonce)
}
}
impl From<u64> for SenderId {

View File

@ -510,6 +510,14 @@ where
self.pool.get_highest_transaction_by_sender(sender)
}
fn get_highest_consecutive_transaction_by_sender(
&self,
sender: Address,
on_chain_nonce: u64,
) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
self.pool.get_highest_consecutive_transaction_by_sender(sender, on_chain_nonce)
}
fn get_transaction_by_sender_and_nonce(
&self,
sender: Address,

View File

@ -227,6 +227,14 @@ impl TransactionPool for NoopTransactionPool {
None
}
fn get_highest_consecutive_transaction_by_sender(
&self,
_sender: Address,
_on_chain_nonce: u64,
) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
None
}
fn get_transaction_by_sender_and_nonce(
&self,
_sender: Address,

View File

@ -785,6 +785,17 @@ where
self.get_pool_data().get_highest_transaction_by_sender(sender_id)
}
/// Returns the transaction with the highest nonce that is executable given the on chain nonce.
pub(crate) fn get_highest_consecutive_transaction_by_sender(
&self,
sender: Address,
on_chain_nonce: u64,
) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
let sender_id = self.get_sender_id(sender);
self.get_pool_data()
.get_highest_consecutive_transaction_by_sender(sender_id.into_id(on_chain_nonce))
}
/// Returns all transactions that where submitted with the given [`TransactionOrigin`]
pub(crate) fn get_transactions_by_origin(
&self,

View File

@ -108,6 +108,27 @@ impl<T: TransactionOrdering> TxPool<T> {
self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
}
/// Returns the transaction with the highest nonce that is executable given the on chain nonce.
///
/// Note: The next pending pooled transaction must have the on chain nonce.
pub(crate) fn get_highest_consecutive_transaction_by_sender(
&self,
on_chain: TransactionId,
) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
let mut last_consecutive_tx = None;
let mut next_expected_nonce = on_chain.nonce;
for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
if next_expected_nonce != id.nonce {
break
}
next_expected_nonce = id.next_nonce();
last_consecutive_tx = Some(tx);
}
last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
}
/// Returns access to the [`AllTransactions`] container.
pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
&self.all_transactions
@ -2755,6 +2776,36 @@ mod tests {
assert_eq!(highest_tx.as_ref().transaction, tx1);
}
#[test]
fn get_highest_consecutive_transaction_by_sender() {
// Set up a mock transaction factory and a new transaction pool.
let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
let mut f = MockTransactionFactory::default();
// Create transactions with nonces 0, 1, 2, 4, 5.
let sender = Address::random();
let txs: Vec<_> = vec![0, 1, 2, 4, 5];
for nonce in txs {
let mut mock_tx = MockTransaction::eip1559();
mock_tx.set_sender(sender);
mock_tx.set_nonce(nonce);
let validated_tx = f.validated(mock_tx);
pool.add_transaction(validated_tx, U256::from(1000), 0).unwrap();
}
// Get last consecutive transaction
let sender_id = f.ids.sender_id(&sender).unwrap();
let next_tx = pool.get_highest_consecutive_transaction_by_sender(sender_id.into_id(0));
assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
let next_tx = pool.get_highest_consecutive_transaction_by_sender(sender_id.into_id(4));
assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
let next_tx = pool.get_highest_consecutive_transaction_by_sender(sender_id.into_id(5));
assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
}
#[test]
fn discard_nonce_too_low() {
let mut f = MockTransactionFactory::default();

View File

@ -357,6 +357,21 @@ pub trait TransactionPool: Send + Sync + Clone {
sender: Address,
) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
/// Returns the transaction with the highest nonce that is executable given the on chain nonce.
/// In other words the highest non nonce gapped transaction.
///
/// Note: The next pending pooled transaction must have the on chain nonce.
///
/// For example, for a given on chain nonce of `5`, the next transaction must have that nonce.
/// If the pool contains txs `[5,6,7]` this returns tx `7`.
/// If the pool contains txs `[6,7]` this returns `None` because the next valid nonce (5) is
/// missing, which means txs `[6,7]` are nonce gapped.
fn get_highest_consecutive_transaction_by_sender(
&self,
sender: Address,
on_chain_nonce: u64,
) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
/// Returns a transaction sent by a given user and a nonce
fn get_transaction_by_sender_and_nonce(
&self,