From 48dd39d86d9d0e780322d16fce7c931ea03e8235 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 26 Jun 2024 01:35:33 +0200 Subject: [PATCH] chore(provider): simplify nonce filler --- crates/provider/src/fillers/nonce.rs | 66 +++++++++++++++++++--------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/crates/provider/src/fillers/nonce.rs b/crates/provider/src/fillers/nonce.rs index 237cb7d95836..5f281fc31d3d 100644 --- a/crates/provider/src/fillers/nonce.rs +++ b/crates/provider/src/fillers/nonce.rs @@ -7,8 +7,10 @@ use alloy_network::{Network, TransactionBuilder}; use alloy_primitives::Address; use alloy_transport::{Transport, TransportResult}; use dashmap::DashMap; -use std::sync::Arc; -use tokio::sync::Mutex; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, +}; /// A [`TxFiller`] that fills nonces on transactions. /// @@ -41,7 +43,7 @@ use tokio::sync::Mutex; /// ``` #[derive(Clone, Debug, Default)] pub struct NonceFiller { - nonces: DashMap>>>, + nonces: DashMap>, } impl TxFiller for NonceFiller { @@ -92,33 +94,57 @@ impl NonceFiller { N: Network, T: Transport + Clone, { - // locks dashmap internally for a short duration to clone the `Arc` - let mutex = Arc::clone(self.nonces.entry(from).or_default().value()); - - // locks the value (does not lock dashmap) - let mut nonce = mutex.lock().await; - match *nonce { - Some(ref mut nonce) => { - *nonce += 1; - Ok(*nonce) - } - None => { - // initialize the nonce if we haven't seen this account before - let initial_nonce = provider.get_transaction_count(from).await?; - *nonce = Some(initial_nonce); - Ok(initial_nonce) - } + // Use `u64::MAX` as a sentinel value to indicate that the nonce has not been fetched yet. + const NONE: u64 = u64::MAX; + + // Locks dashmap internally for a short duration to clone the `Arc`. + // We also don't want to hold the dashmap lock through the await point below. + let nonce = Arc::clone( + self.nonces.entry(from).or_insert_with(|| Arc::new(AtomicU64::new(NONE))).value(), + ); + + let prev_nonce = nonce.fetch_add(1, Ordering::AcqRel); + let current_nonce; + if prev_nonce == NONE { + // Initialize the nonce if we haven't seen this account before. + current_nonce = provider.get_transaction_count(from).await?; + nonce.store(current_nonce, Ordering::Release); + } else { + current_nonce = prev_nonce + 1; + // `nonce` is already incremented in the `fetch_add` call above. } + Ok(current_nonce) } } #[cfg(test)] mod tests { use super::*; - use crate::{ProviderBuilder, WalletProvider}; + use crate::{ext::AnvilApi, ProviderBuilder, WalletProvider}; use alloy_primitives::{address, U256}; use alloy_rpc_types_eth::TransactionRequest; + #[tokio::test] + async fn smoke_test() { + let filler = NonceFiller::default(); + let provider = ProviderBuilder::new().on_anvil(); + let address = Address::ZERO; + for i in 0..5 { + let nonce = filler.get_next_nonce(&provider, address).await.unwrap(); + assert_eq!(nonce, i); + } + + #[cfg(feature = "anvil-api")] + { + filler.nonces.clear(); + provider.anvil_set_nonce(address, U256::from(69)).await.unwrap(); + for i in 0..5 { + let nonce = filler.get_next_nonce(&provider, address).await.unwrap(); + assert_eq!(nonce, 69 + i); + } + } + } + #[tokio::test] async fn no_nonce_if_sender_unset() { let provider = ProviderBuilder::new().with_nonce_management().on_anvil();