From e54666b467c9cc5848873935622930449075828a Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Sat, 4 Mar 2023 17:37:50 -0800 Subject: [PATCH] fix: init guard in noncemanager (#2227) --- ethers-middleware/src/nonce_manager.rs | 43 +++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/ethers-middleware/src/nonce_manager.rs b/ethers-middleware/src/nonce_manager.rs index 1e59c57a9..996f30f58 100644 --- a/ethers-middleware/src/nonce_manager.rs +++ b/ethers-middleware/src/nonce_manager.rs @@ -9,6 +9,7 @@ use thiserror::Error; /// consecutive transactions without waiting for them to hit the mempool pub struct NonceManagerMiddleware { inner: M, + init_guard: futures_locks::Mutex<()>, initialized: AtomicBool, nonce: AtomicU64, address: Address, @@ -21,7 +22,13 @@ where /// Instantiates the nonce manager with a 0 nonce. The `address` should be the /// address which you'll be sending transactions from pub fn new(inner: M, address: Address) -> Self { - Self { initialized: false.into(), nonce: 0.into(), inner, address } + Self { + inner, + init_guard: Default::default(), + initialized: Default::default(), + nonce: Default::default(), + address, + } } /// Returns the next nonce to be used @@ -34,21 +41,29 @@ where &self, block: Option, ) -> Result> { - // initialize the nonce the first time the manager is called - if !self.initialized.load(Ordering::SeqCst) { - let nonce = self - .inner - .get_transaction_count(self.address, block) - .await - .map_err(MiddlewareError::from_err)?; - self.nonce.store(nonce.as_u64(), Ordering::SeqCst); - self.initialized.store(true, Ordering::SeqCst); - Ok(nonce) - } else { + if self.initialized.load(Ordering::SeqCst) { // return current nonce - Ok(self.nonce.load(Ordering::SeqCst).into()) + return Ok(self.nonce.load(Ordering::SeqCst).into()) } - } + + let _guard = self.init_guard.lock().await; + + // do this again in case multiple tasks enter this codepath + if self.initialized.load(Ordering::SeqCst) { + // return current nonce + return Ok(self.nonce.load(Ordering::SeqCst).into()) + } + + // initialize the nonce the first time the manager is called + let nonce = self + .inner + .get_transaction_count(self.address, block) + .await + .map_err(MiddlewareError::from_err)?; + self.nonce.store(nonce.as_u64(), Ordering::SeqCst); + self.initialized.store(true, Ordering::SeqCst); + Ok(nonce) + } // guard dropped here async fn get_transaction_count_with_manager( &self,