minip2p_platform/
std_impl.rs1use std::time::{Instant, SystemTime, UNIX_EPOCH};
2
3use crate::{Clock, EntropyError, EntropySource, Now};
4
5#[derive(Clone, Copy, Debug)]
25pub struct StdClock {
26 epoch: Instant,
27}
28
29impl StdClock {
30 pub fn new() -> Self {
32 Self {
33 epoch: Instant::now(),
34 }
35 }
36
37 pub fn with_epoch(epoch: Instant) -> Self {
42 Self { epoch }
43 }
44
45 pub fn epoch(&self) -> Instant {
47 self.epoch
48 }
49}
50
51impl Default for StdClock {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl Clock for StdClock {
58 fn now(&mut self) -> Now {
59 let monotonic_ms = u64::try_from(self.epoch.elapsed().as_millis())
63 .unwrap_or(u64::MAX)
64 .min(Now::MAX_MONOTONIC_MS);
65 let unix_seconds = SystemTime::now()
68 .duration_since(UNIX_EPOCH)
69 .ok()
70 .map(|since_epoch| since_epoch.as_secs());
71
72 Now {
73 monotonic_ms,
74 unix_seconds,
75 }
76 }
77}
78
79#[derive(Clone, Copy, Debug, Default)]
91pub struct StdEntropy;
92
93impl StdEntropy {
94 pub const fn new() -> Self {
96 Self
97 }
98}
99
100impl EntropySource for StdEntropy {
101 fn fill_bytes(&mut self, output: &mut [u8]) -> Result<(), EntropyError> {
102 getrandom::fill(output).map_err(|error| match error.raw_os_error() {
103 Some(code) => EntropyError::failed_with_code("os entropy source failed", code),
104 None => EntropyError::failed("os entropy source failed"),
105 })
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use std::thread::sleep;
113 use std::time::Duration;
114
115 #[test]
116 fn monotonic_time_starts_near_the_epoch_and_advances() {
117 let mut clock = StdClock::new();
118 let start = clock.now();
119 assert!(start.monotonic_ms < 1_000, "expected a fresh timeline");
120
121 sleep(Duration::from_millis(5));
122 let later = clock.now();
123 assert!(later.monotonic_ms >= start.monotonic_ms + 5);
124 }
125
126 #[test]
127 fn samples_never_decrease() {
128 let mut clock = StdClock::new();
129 let mut previous = clock.now();
130 for _ in 0..100 {
131 let current = clock.now();
132 assert!(current.monotonic_ms >= previous.monotonic_ms);
133 previous = current;
134 }
135 }
136
137 #[test]
138 fn samples_are_measured_from_the_injected_epoch() {
139 const OFFSET_MS: u64 = 5_000;
140
141 let epoch = Instant::now();
142 let mut shared = StdClock::with_epoch(epoch);
143 let mut older = StdClock::with_epoch(epoch - Duration::from_millis(OFFSET_MS));
146
147 let from_shared = shared.now().monotonic_ms;
148 let from_older = older.now().monotonic_ms;
149
150 let delta = from_older
151 .checked_sub(from_shared)
152 .expect("older epoch must read further along the timeline");
153 assert!(
154 delta.abs_diff(OFFSET_MS) < 1_000,
155 "expected ~{OFFSET_MS}ms between epochs, got {delta}ms"
156 );
157 }
158
159 #[test]
160 fn clocks_sharing_an_epoch_produce_comparable_samples() {
161 let epoch = Instant::now();
162 let mut first = StdClock::with_epoch(epoch);
163 let mut second = StdClock::with_epoch(epoch);
164 assert_eq!(first.epoch(), second.epoch());
165
166 let before = first.now().monotonic_ms;
167 sleep(Duration::from_millis(5));
168 let after = second.now().monotonic_ms;
169
170 assert!(
175 after >= before + 5,
176 "sleep not observable across clocks: {before} -> {after}"
177 );
178 }
179
180 #[test]
181 fn reports_a_plausible_wall_clock() {
182 let mut clock = StdClock::new();
183 let unix_seconds = clock.now().unix_seconds.expect("hosts have a wall clock");
184 assert!(unix_seconds > 1_577_836_800);
186 }
187
188 #[test]
189 fn entropy_fills_the_whole_buffer() {
190 const SENTINEL: u8 = 0x5a;
196 const WINDOW: usize = 16;
197
198 let mut entropy = StdEntropy::new();
199 let mut buffer = [SENTINEL; 256];
200 entropy.fill_bytes(&mut buffer).expect("os entropy");
201
202 for (index, window) in buffer.windows(WINDOW).enumerate() {
203 assert!(
204 window.iter().any(|&byte| byte != SENTINEL),
205 "bytes {index}..{} were left unwritten",
206 index + WINDOW
207 );
208 }
209 }
210
211 #[test]
212 fn entropy_does_not_repeat_itself() {
213 let mut entropy = StdEntropy::new();
214 let first = entropy.next_u64().expect("os entropy");
215 let second = entropy.next_u64().expect("os entropy");
216 assert_ne!(first, second);
217 }
218
219 #[test]
220 fn empty_buffer_is_not_an_error() {
221 let mut entropy = StdEntropy::new();
222 entropy.fill_bytes(&mut []).expect("empty fill");
223 }
224}