util.rs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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. use std::{
  19. collections::HashMap,
  20. fs::{File, OpenOptions},
  21. io::Write,
  22. time::UNIX_EPOCH,
  23. };
  24. use darkfi_serial::{deserialize, deserialize_async, serialize};
  25. use log::error;
  26. use tinyjson::JsonValue;
  27. use crate::{
  28. event_graph::{Event, GENESIS_CONTENTS, INITIAL_GENESIS, NULL_ID, N_EVENT_PARENTS},
  29. rpc::{
  30. jsonrpc::{ErrorCode, JsonError, JsonResponse, JsonResult},
  31. util::json_map,
  32. },
  33. util::{encoding::base64, file::load_file, path::expand_path},
  34. Result,
  35. };
  36. /// Seconds in a day
  37. pub(super) const DAY: i64 = 86400;
  38. /// Calculate the midnight timestamp given a number of days.
  39. /// If `days` is 0, calculate the midnight timestamp of today.
  40. pub(super) fn midnight_timestamp(days: i64) -> u64 {
  41. // Get current time
  42. let now = UNIX_EPOCH.elapsed().unwrap().as_secs() as i64;
  43. // Find the timestamp for the midnight of the current day
  44. let cur_midnight = (now / DAY) * DAY;
  45. // Adjust for days_from_now
  46. (cur_midnight + (DAY * days)) as u64
  47. }
  48. /// Calculate the number of days since a given midnight timestamp.
  49. pub(super) fn days_since(midnight_ts: u64) -> u64 {
  50. // Get current time
  51. let now = UNIX_EPOCH.elapsed().unwrap().as_secs();
  52. // Calculate the difference between the current timestamp
  53. // and the given midnight timestamp
  54. let elapsed_seconds = now - midnight_ts;
  55. // Convert the elapsed seconds into days
  56. elapsed_seconds / DAY as u64
  57. }
  58. /// Calculate the timestamp of the next DAG rotation.
  59. pub(super) fn next_rotation_timestamp(starting_timestamp: u64, rotation_period: u64) -> u64 {
  60. // Prevent division by 0
  61. if rotation_period == 0 {
  62. panic!("Rotation period cannot be 0");
  63. }
  64. // Calculate the number of days since the given starting point
  65. let days_passed = days_since(starting_timestamp);
  66. // Find out how many rotation periods have occurred since
  67. // the starting point.
  68. // Note: when rotation_period = 1, rotations_since_start = days_passed
  69. let rotations_since_start = (days_passed + rotation_period - 1) / rotation_period;
  70. // Find out the number of days until the next rotation. Panic if result is beyond the range
  71. // of i64.
  72. let days_until_next_rotation: i64 =
  73. (rotations_since_start * rotation_period - days_passed).try_into().unwrap();
  74. // Get the timestamp for the next rotation
  75. if days_until_next_rotation == 0 {
  76. // If there are 0 days until the next rotation, we want
  77. // to rotate tomorrow, at midnight. This is a special case.
  78. return midnight_timestamp(1)
  79. }
  80. midnight_timestamp(days_until_next_rotation)
  81. }
  82. /// Calculate the time in seconds until the next_rotation, given
  83. /// as a timestamp.
  84. /// `next_rotation` here represents a timestamp in UNIX epoch format.
  85. pub(super) fn seconds_until_next_rotation(next_rotation: u64) -> u64 {
  86. // Store `now` in a variable in order to avoid a TOCTOU error.
  87. // There may be a drift of one second between this panic check and
  88. // the return value if we get unlucky.
  89. let now = UNIX_EPOCH.elapsed().unwrap().as_secs();
  90. if next_rotation < now {
  91. panic!("Next rotation timestamp is in the past");
  92. }
  93. next_rotation - now
  94. }
  95. /// Generate a deterministic genesis event corresponding to the DAG's configuration.
  96. pub(super) fn generate_genesis(days_rotation: u64) -> Event {
  97. // Days rotation is u64 except zero
  98. let timestamp = if days_rotation == 0 {
  99. INITIAL_GENESIS
  100. } else {
  101. // First check how many days passed since initial genesis.
  102. let days_passed = days_since(INITIAL_GENESIS);
  103. // Calculate the number of days_rotation intervals since INITIAL_GENESIS
  104. let rotations_since_genesis = days_passed / days_rotation;
  105. // Calculate the timestamp of the most recent event
  106. INITIAL_GENESIS + (rotations_since_genesis * days_rotation * DAY as u64)
  107. };
  108. Event {
  109. timestamp,
  110. content: GENESIS_CONTENTS.to_vec(),
  111. parents: [NULL_ID; N_EVENT_PARENTS],
  112. layer: 0,
  113. }
  114. }
  115. pub(super) fn replayer_log(cmd: String, value: Vec<u8>) -> Result<()> {
  116. let mut replayer_log_file = expand_path("/tmp")?;
  117. replayer_log_file.push("replayer.log");
  118. if !replayer_log_file.exists() {
  119. File::create(&replayer_log_file)?;
  120. };
  121. let mut file = OpenOptions::new().append(true).open(&replayer_log_file)?;
  122. let v = base64::encode(&value);
  123. let f = format!("{cmd} {v}");
  124. writeln!(file, "{}", f)?;
  125. Ok(())
  126. }
  127. pub async fn recreate_from_replayer_log() -> JsonResult {
  128. let mut replayer_log_file = expand_path("/tmp").unwrap();
  129. replayer_log_file.push("replayer.log");
  130. if !replayer_log_file.exists() {
  131. error!("Error loading replaied log");
  132. return JsonResult::Error(JsonError::new(
  133. ErrorCode::ParseError,
  134. Some("Error loading replaied log".to_string()),
  135. 1,
  136. ))
  137. };
  138. let reader = load_file(&replayer_log_file).unwrap();
  139. let datastore = expand_path("/tmp/replayed_db").unwrap();
  140. let sled_db = sled::open(datastore).unwrap();
  141. let dag = sled_db.open_tree("replayer").unwrap();
  142. for line in reader.lines() {
  143. let line = line.split(' ').collect::<Vec<&str>>();
  144. if line[0] == "insert" {
  145. let v = base64::decode(line[1]).unwrap();
  146. let v: Event = deserialize(&v).unwrap();
  147. let v_se = serialize(&v);
  148. dag.insert(v.id().as_bytes(), v_se).unwrap();
  149. }
  150. }
  151. let mut graph = HashMap::new();
  152. for iter_elem in dag.iter() {
  153. let (id, val) = iter_elem.unwrap();
  154. let id = blake3::Hash::from_bytes((&id as &[u8]).try_into().unwrap());
  155. let val: Event = deserialize_async(&val).await.unwrap();
  156. graph.insert(id, val);
  157. }
  158. let json_graph = graph
  159. .into_iter()
  160. .map(|(k, v)| {
  161. let key = k.to_string();
  162. let value = JsonValue::from(v);
  163. (key, value)
  164. })
  165. .collect();
  166. let values = json_map([("dag", JsonValue::Object(json_graph))]);
  167. let result = JsonValue::Object(HashMap::from([("eventgraph_info".to_string(), values)]));
  168. JsonResponse::new(result, 1).into()
  169. }
  170. #[cfg(test)]
  171. mod tests {
  172. use super::*;
  173. #[test]
  174. fn test_days_since() {
  175. let five_days_ago = midnight_timestamp(-5);
  176. assert_eq!(days_since(five_days_ago), 5);
  177. let today = midnight_timestamp(0);
  178. assert_eq!(days_since(today), 0);
  179. }
  180. #[test]
  181. fn test_next_rotation_timestamp() {
  182. let starting_point = midnight_timestamp(-10);
  183. let rotation_period = 7;
  184. // The first rotation since the starting point would be 3 days ago.
  185. // So the next rotation should be 4 days from now.
  186. let expected = midnight_timestamp(4);
  187. assert_eq!(next_rotation_timestamp(starting_point, rotation_period), expected);
  188. // When starting from today with a rotation period of 1 (day),
  189. // we should get tomorrow's timestamp.
  190. // This is a special case.
  191. let midnight_today: u64 = midnight_timestamp(0);
  192. let midnight_tomorrow = midnight_today + 86400u64; // add a day, in seconds
  193. assert_eq!(midnight_tomorrow, next_rotation_timestamp(midnight_today, 1));
  194. }
  195. #[test]
  196. #[should_panic]
  197. fn test_next_rotation_timestamp_panics_on_overflow() {
  198. next_rotation_timestamp(0, u64::MAX);
  199. }
  200. #[test]
  201. #[should_panic]
  202. fn test_next_rotation_timestamp_panics_on_division_by_zero() {
  203. next_rotation_timestamp(0, 0);
  204. }
  205. #[test]
  206. fn test_seconds_until_next_rotation_is_within_rotation_interval() {
  207. let days_rotation = 1u64;
  208. // The amount of time in seconds between rotations.
  209. let rotation_interval = days_rotation * 86400u64;
  210. let next_rotation_timestamp = next_rotation_timestamp(INITIAL_GENESIS, days_rotation);
  211. let s = seconds_until_next_rotation(next_rotation_timestamp);
  212. assert!(s < rotation_interval);
  213. }
  214. }