use alloy::primitives::Address;
use dashmap::DashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug)]
pub struct NonceManager {
counters: DashMap<Address, AtomicU64>,
global_counter: AtomicU64,
isolate_per_address: bool,
}
impl NonceManager {
pub fn new(isolate_per_address: bool) -> Self {
Self {
counters: DashMap::new(),
global_counter: AtomicU64::new(0),
isolate_per_address,
}
}
pub fn next_nonce(&self, address: Option<Address>) -> u64 {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time before UNIX epoch")
.as_millis() as u64;
let counter = match (self.isolate_per_address, address) {
(true, Some(addr)) => self
.counters
.entry(addr)
.or_insert_with(|| AtomicU64::new(0))
.fetch_add(1, Ordering::Relaxed),
_ => self.global_counter.fetch_add(1, Ordering::Relaxed),
};
now.saturating_add(counter % 1000)
}
pub fn reset_address(&self, address: Address) {
if let Some(counter) = self.counters.get_mut(&address) {
counter.store(0, Ordering::Relaxed);
}
}
pub fn get_counter(&self, address: Option<Address>) -> u64 {
if let Some(addr) = address {
if self.isolate_per_address {
self.counters
.get(&addr)
.map(|c| c.load(Ordering::Relaxed))
.unwrap_or(0)
} else {
self.global_counter.load(Ordering::Relaxed)
}
} else {
self.global_counter.load(Ordering::Relaxed)
}
}
pub fn is_valid_nonce(nonce: u64) -> bool {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time before UNIX epoch")
.as_millis() as u64;
const TWO_DAYS_MS: u64 = 2 * 24 * 60 * 60 * 1000;
const ONE_DAY_MS: u64 = 24 * 60 * 60 * 1000;
nonce > now.saturating_sub(TWO_DAYS_MS) && nonce < now.saturating_add(ONE_DAY_MS)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nonce_uniqueness() {
let manager = NonceManager::new(false);
let nonce1 = manager.next_nonce(None);
let nonce2 = manager.next_nonce(None);
assert_ne!(nonce1, nonce2);
assert!(nonce2 > nonce1);
}
#[test]
fn test_address_isolation() {
let manager = NonceManager::new(true);
let addr1 = Address::new([1u8; 20]);
let addr2 = Address::new([2u8; 20]);
let n1_1 = manager.next_nonce(Some(addr1));
let n2_1 = manager.next_nonce(Some(addr2));
let n1_2 = manager.next_nonce(Some(addr1));
let n2_2 = manager.next_nonce(Some(addr2));
assert!(n1_2 > n1_1);
assert!(n2_2 > n2_1);
assert_eq!(manager.get_counter(Some(addr1)), 2);
assert_eq!(manager.get_counter(Some(addr2)), 2);
}
#[test]
fn test_nonce_validity() {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time before UNIX epoch")
.as_millis() as u64;
assert!(NonceManager::is_valid_nonce(now));
assert!(NonceManager::is_valid_nonce(now - 24 * 60 * 60 * 1000));
assert!(!NonceManager::is_valid_nonce(now - 3 * 24 * 60 * 60 * 1000));
assert!(!NonceManager::is_valid_nonce(now + 2 * 24 * 60 * 60 * 1000));
}
}