kraken_api_client/auth/
nonce.rs1use std::sync::atomic::{AtomicU64, Ordering};
7use std::time::{SystemTime, UNIX_EPOCH};
8
9pub trait NonceProvider: Send + Sync {
14 fn next_nonce(&self) -> u64;
18}
19
20pub struct IncreasingNonce {
25 last_nonce: AtomicU64,
26}
27
28impl IncreasingNonce {
29 pub fn new() -> Self {
31 Self {
32 last_nonce: AtomicU64::new(0),
33 }
34 }
35
36 fn current_time_micros() -> u64 {
38 SystemTime::now()
39 .duration_since(UNIX_EPOCH)
40 .unwrap_or_default()
41 .as_micros() as u64
42 }
43}
44
45impl Default for IncreasingNonce {
46 fn default() -> Self {
47 Self::new()
48 }
49}
50
51impl NonceProvider for IncreasingNonce {
52 fn next_nonce(&self) -> u64 {
53 let time_nonce = Self::current_time_micros();
54
55 loop {
58 let last = self.last_nonce.load(Ordering::SeqCst);
59 let next = time_nonce.max(last + 1);
60
61 if self
62 .last_nonce
63 .compare_exchange(last, next, Ordering::SeqCst, Ordering::SeqCst)
64 .is_ok()
65 {
66 return next;
67 }
68 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use std::collections::HashSet;
77 use std::thread;
78
79 #[test]
80 fn test_nonce_strictly_increasing() {
81 let provider = IncreasingNonce::new();
82
83 let mut last = 0u64;
84 for _ in 0..1000 {
85 let nonce = provider.next_nonce();
86 assert!(nonce > last, "Nonce must be strictly increasing");
87 last = nonce;
88 }
89 }
90
91 #[test]
92 fn test_nonce_unique_across_threads() {
93 let provider = std::sync::Arc::new(IncreasingNonce::new());
94 let mut handles = vec![];
95
96 for _ in 0..4 {
97 let p = provider.clone();
98 handles.push(thread::spawn(move || {
99 let mut nonces = Vec::new();
100 for _ in 0..1000 {
101 nonces.push(p.next_nonce());
102 }
103 nonces
104 }));
105 }
106
107 let mut all_nonces = HashSet::new();
108 for handle in handles {
109 let nonces = handle.join().unwrap();
110 for nonce in nonces {
111 assert!(
112 all_nonces.insert(nonce),
113 "Nonce must be unique across threads"
114 );
115 }
116 }
117 }
118}