lighter_rust/
nonce.rs

1use crate::error::{LighterError, Result};
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::time::{SystemTime, UNIX_EPOCH};
4
5#[derive(Debug)]
6pub struct NonceManager {
7    counter: AtomicU64,
8    last_timestamp: AtomicU64,
9}
10
11impl NonceManager {
12    pub fn new() -> Self {
13        Self {
14            counter: AtomicU64::new(0),
15            last_timestamp: AtomicU64::new(0),
16        }
17    }
18
19    pub fn generate(&self) -> Result<u64> {
20        let current_timestamp = SystemTime::now()
21            .duration_since(UNIX_EPOCH)
22            .map_err(|e| LighterError::Nonce(format!("Time error: {}", e)))?
23            .as_millis() as u64;
24
25        let last_timestamp = self.last_timestamp.load(Ordering::Acquire);
26
27        if current_timestamp > last_timestamp {
28            self.last_timestamp
29                .store(current_timestamp, Ordering::Release);
30            self.counter.store(0, Ordering::Release);
31            Ok(current_timestamp * 1000)
32        } else {
33            let counter = self.counter.fetch_add(1, Ordering::AcqRel);
34            if counter >= 999 {
35                return Err(LighterError::Nonce(
36                    "Too many nonces generated in the same millisecond".to_string(),
37                ));
38            }
39            Ok(last_timestamp * 1000 + counter + 1)
40        }
41    }
42}
43
44impl Default for NonceManager {
45    fn default() -> Self {
46        Self::new()
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use std::collections::HashSet;
54    use std::sync::Arc;
55    use std::thread;
56
57    #[test]
58    fn test_nonce_uniqueness() {
59        let manager = NonceManager::new();
60        let mut nonces = HashSet::new();
61
62        // Generate 100 nonces and ensure they're all unique
63        for _ in 0..100 {
64            let nonce = manager.generate().unwrap();
65            assert!(nonces.insert(nonce), "Duplicate nonce generated: {}", nonce);
66        }
67    }
68
69    #[test]
70    fn test_nonce_increasing() {
71        let manager = NonceManager::new();
72        let mut previous = 0;
73
74        // Generate nonces and ensure they're increasing
75        for _ in 0..50 {
76            let nonce = manager.generate().unwrap();
77            assert!(
78                nonce > previous,
79                "Nonce {} is not greater than previous {}",
80                nonce,
81                previous
82            );
83            previous = nonce;
84        }
85    }
86
87    #[test]
88    fn test_nonce_concurrent_generation() {
89        let manager = Arc::new(NonceManager::new());
90        let mut handles = vec![];
91        let nonces = Arc::new(std::sync::Mutex::new(HashSet::new()));
92
93        // Spawn multiple threads generating nonces concurrently
94        for _ in 0..10 {
95            let manager_clone = Arc::clone(&manager);
96            let nonces_clone = Arc::clone(&nonces);
97
98            let handle = thread::spawn(move || {
99                for _ in 0..10 {
100                    let nonce = manager_clone.generate().unwrap();
101                    let mut nonces_guard = nonces_clone.lock().unwrap();
102                    assert!(
103                        nonces_guard.insert(nonce),
104                        "Duplicate nonce in concurrent generation: {}",
105                        nonce
106                    );
107                }
108            });
109
110            handles.push(handle);
111        }
112
113        // Wait for all threads to complete
114        for handle in handles {
115            handle.join().unwrap();
116        }
117
118        // Verify we generated exactly 100 unique nonces
119        let final_nonces = nonces.lock().unwrap();
120        assert_eq!(final_nonces.len(), 100);
121    }
122
123    #[test]
124    fn test_nonce_timestamp_format() {
125        let manager = NonceManager::new();
126        let nonce = manager.generate().unwrap();
127
128        // Nonce should be a timestamp in microseconds (13+ digits)
129        assert!(
130            nonce > 1_000_000_000_000,
131            "Nonce should be at least 13 digits"
132        );
133
134        // The nonce should be close to current time in microseconds
135        let current_time_micros = SystemTime::now()
136            .duration_since(UNIX_EPOCH)
137            .unwrap()
138            .as_millis() as u64
139            * 1000;
140
141        // Allow for 1 second difference due to execution time
142        assert!(nonce <= current_time_micros + 1_000_000);
143        assert!(nonce >= current_time_micros - 1_000_000);
144    }
145
146    #[test]
147    fn test_nonce_counter_reset() {
148        let manager = NonceManager::new();
149
150        // Generate a nonce
151        let first_nonce = manager.generate().unwrap();
152
153        // Sleep for a bit to ensure timestamp changes
154        std::thread::sleep(std::time::Duration::from_millis(2));
155
156        // Generate another nonce - counter should reset with new timestamp
157        let second_nonce = manager.generate().unwrap();
158
159        // The second nonce should be based on a new timestamp
160        assert!(second_nonce > first_nonce);
161
162        // The difference should be at least 1 millisecond in microseconds (1000)
163        assert!(second_nonce - first_nonce >= 1000);
164    }
165
166    #[test]
167    fn test_nonce_rapid_generation() {
168        let manager = NonceManager::new();
169        let mut nonces = Vec::new();
170
171        // Generate many nonces rapidly in the same millisecond
172        for _ in 0..100 {
173            match manager.generate() {
174                Ok(nonce) => nonces.push(nonce),
175                Err(e) => {
176                    // If we hit the limit, that's expected behavior
177                    if let LighterError::Nonce(msg) = e {
178                        assert!(msg.contains("Too many nonces"));
179                        break;
180                    } else {
181                        panic!("Unexpected error: {}", e);
182                    }
183                }
184            }
185        }
186
187        // Verify all generated nonces are unique
188        let unique_nonces: HashSet<_> = nonces.iter().collect();
189        assert_eq!(unique_nonces.len(), nonces.len());
190    }
191
192    #[test]
193    fn test_nonce_default_impl() {
194        let manager1 = NonceManager::new();
195        let manager2 = NonceManager::default();
196
197        // Both should generate valid nonces
198        assert!(manager1.generate().is_ok());
199        assert!(manager2.generate().is_ok());
200    }
201}