1use std::{net::UdpSocket, time::Duration};
21
22use tracing::debug;
23use url::Url;
24
25use crate::{util::time::Timestamp, Error, Result};
26
27const RETRIES: u8 = 10;
29const NTP_ADDRESS: &str = "pool.ntp.org:123";
31const EPOCH: u32 = 2208988800; pub async fn ntp_request() -> Result<Timestamp> {
35 let sock = UdpSocket::bind("0.0.0.0:0")?;
37 sock.set_read_timeout(Some(Duration::from_secs(5)))?;
38 sock.set_write_timeout(Some(Duration::from_secs(5)))?;
39
40 let mut packet = [0u8; 48];
42 packet[0] = (3 << 6) | (4 << 3) | 3;
43 sock.send_to(&packet, NTP_ADDRESS)?;
44
45 sock.recv(&mut packet[..])?;
47 let (bytes, _) = packet[40..44].split_at(core::mem::size_of::<u32>());
48 let num = u32::from_be_bytes(bytes.try_into().unwrap());
49 let timestamp = Timestamp::from_u64((num - EPOCH) as u64);
50
51 Ok(timestamp)
52}
53
54pub async fn check_clock(peers: &[Url]) -> Result<()> {
60 debug!(target: "rpc::clock_sync", "System clock check started...");
61 let mut r = 0;
62 while r < RETRIES {
63 if let Err(e) = clock_check(peers).await {
64 debug!(target: "rpc::clock_sync", "Error during clock check: {e:#?}");
65 r += 1;
66 continue
67 };
68 break
69 }
70
71 debug!(target: "rpc::clock_sync", "System clock check finished. Retries: {r}");
72 if r == RETRIES {
73 return Err(Error::InvalidClock)
74 }
75
76 Ok(())
77}
78
79async fn clock_check(_peers: &[Url]) -> Result<()> {
80 let requests_start = Timestamp::current_time();
82 let peer_time = Some(Timestamp::current_time());
85
86 let ntp_request_start = Timestamp::current_time();
88 let ntp_time = ntp_request().await?;
90
91 let ntp_elapsed_time = ntp_request_start.elapsed()?;
93 let requests_elapsed_time = requests_start.elapsed()?;
94
95 let system_time = Timestamp::current_time();
97
98 let ntp_time = ntp_time.checked_add(ntp_elapsed_time)?;
100 let peer_time = match peer_time {
101 None => None,
102 Some(p) => Some(p.checked_add(requests_elapsed_time)?),
103 };
104
105 debug!(target: "rpc::clock_sync", "peer_time: {peer_time:#?}");
106 debug!(target: "rpc::clock_sync", "ntp_time: {ntp_time:#?}");
107 debug!(target: "rpc::clock_sync", "system_time: {system_time:#?}");
108
109 let check = match peer_time {
111 Some(p) => (system_time == p) && (system_time == ntp_time),
112 None => system_time == ntp_time,
113 };
114
115 match check {
116 true => Ok(()),
117 false => Err(Error::InvalidClock),
118 }
119}