clock_sync.rs
1 /* This file is part of DarkFi (https://dark.fi) 2 * 3 * Copyright (C) 2020-2025 Dyne.org foundation 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation, either version 3 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19 //! Clock sync module 20 use std::{net::UdpSocket, time::Duration}; 21 22 use tracing::debug; 23 use url::Url; 24 25 use crate::{util::time::Timestamp, Error, Result}; 26 27 /// Clock sync parameters 28 const RETRIES: u8 = 10; 29 /// TODO: Loop through set of ntps, get their average response concurrenyly. 30 const NTP_ADDRESS: &str = "pool.ntp.org:123"; 31 const EPOCH: u32 = 2208988800; // 1900 32 33 /// Raw NTP request execution 34 pub async fn ntp_request() -> Result<Timestamp> { 35 // Create socket 36 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 // Execute request 41 let mut packet = [0u8; 48]; 42 packet[0] = (3 << 6) | (4 << 3) | 3; 43 sock.send_to(&packet, NTP_ADDRESS)?; 44 45 // Parse response 46 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 54 /// This is a very simple check to verify that the system time is correct. 55 /// 56 /// Retry loop is used in case discrepancies are found. 57 /// If all retries fail, system clock is considered invalid. 58 /// TODO: 1. Add proxy functionality in order not to leak connections 59 pub 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 79 async fn clock_check(_peers: &[Url]) -> Result<()> { 80 // Start elapsed time counter to cover for all requests and processing time 81 let requests_start = Timestamp::current_time(); 82 // Poll one of the peers for their current UTC timestamp 83 //let peer_time = peer_request(peers).await?; 84 let peer_time = Some(Timestamp::current_time()); 85 86 // Start elapsed time counter to cover for NTP request and processing time 87 let ntp_request_start = Timestamp::current_time(); 88 // Poll ntp.org for current timestamp 89 let ntp_time = ntp_request().await?; 90 91 // Stop elapsed time counters 92 let ntp_elapsed_time = ntp_request_start.elapsed()?; 93 let requests_elapsed_time = requests_start.elapsed()?; 94 95 // Current system time 96 let system_time = Timestamp::current_time(); 97 98 // Add elapsed time to response times 99 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 // We verify that system time is equal to peer (if exists) and ntp times 110 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 }