ant_bootstrap/cache_store/
cache_data_v1.rs1use crate::Error;
10
11use atomic_write_file::AtomicWriteFile;
12use libp2p::{Multiaddr, PeerId};
13use serde::{Deserialize, Serialize};
14use std::{
15 collections::VecDeque,
16 fs::{self, OpenOptions},
17 io::{Read, Write},
18 path::{Path, PathBuf},
19 time::SystemTime,
20};
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct CacheData {
24 pub peers: VecDeque<(PeerId, VecDeque<Multiaddr>)>,
25 pub last_updated: SystemTime,
26 pub network_version: String,
27 pub cache_version: String,
28}
29
30impl CacheData {
31 pub const CACHE_DATA_VERSION: u32 = 1;
34
35 pub fn sync(&mut self, other: &CacheData, max_addrs_per_peer: usize, max_peers: usize) {
37 for (other_peer, other_addrs) in other.peers.iter() {
38 if other_addrs.is_empty() {
39 continue;
40 }
41 for (peer, addrs) in self.peers.iter_mut() {
42 if peer == other_peer {
43 for addr in other_addrs.iter() {
44 if !addrs.contains(addr) {
45 addrs.push_back(addr.clone());
46 }
47 }
48 while addrs.len() > max_addrs_per_peer {
49 addrs.pop_front();
50 }
51 break;
52 }
53 }
54
55 self.peers.push_back((*other_peer, other_addrs.clone()));
56
57 while self.peers.len() > max_peers {
58 self.peers.pop_front();
59 }
60 }
61
62 self.last_updated = SystemTime::now();
63 }
64
65 pub fn add_peer<'a>(
67 &mut self,
68 peer_id: PeerId,
69 addrs: impl Iterator<Item = &'a Multiaddr>,
70 max_addrs_per_peer: usize,
71 max_peers: usize,
72 ) {
73 if let Some((_, present_addrs)) = self.peers.iter_mut().find(|(id, _)| id == &peer_id) {
74 for addr in addrs {
75 if !present_addrs.contains(addr) {
76 present_addrs.push_back(addr.clone());
77 }
78 }
79 while present_addrs.len() > max_addrs_per_peer {
80 present_addrs.pop_front();
81 }
82 } else {
83 self.peers.push_back((
84 peer_id,
85 addrs
86 .into_iter()
87 .take(max_addrs_per_peer)
88 .cloned()
89 .collect(),
90 ));
91 }
92
93 while self.peers.len() > max_peers {
94 self.peers.pop_front();
95 }
96 }
97
98 pub fn get_all_addrs(&self) -> impl Iterator<Item = &Multiaddr> {
99 self.peers
100 .iter()
101 .flat_map(|(_, bootstrap_addresses)| bootstrap_addresses.iter().next())
102 }
103
104 pub fn read_from_file(cache_dir: &Path, file_name: &str) -> Result<Self, Error> {
105 let file_path = Self::cache_file_path(cache_dir, file_name);
106 let mut file = OpenOptions::new()
108 .read(true)
109 .open(&file_path)
110 .inspect_err(|err| warn!("Failed to open cache file at {file_path:?} : {err}",))?;
111
112 let mut contents = String::new();
114 file.read_to_string(&mut contents).inspect_err(|err| {
115 warn!("Failed to read cache file: {err}");
116 })?;
117
118 let data = serde_json::from_str::<Self>(&contents).map_err(|err| {
120 warn!("Failed to parse cache data: {err}");
121 Error::FailedToParseCacheData
122 })?;
123
124 Ok(data)
125 }
126
127 pub fn write_to_file(&self, cache_dir: &Path, file_name: &str) -> Result<(), Error> {
128 let file_path = Self::cache_file_path(cache_dir, file_name);
129
130 if let Some(parent) = file_path.parent() {
132 fs::create_dir_all(parent)?;
133 }
134
135 let mut file = AtomicWriteFile::options()
136 .open(&file_path)
137 .inspect_err(|err| {
138 error!("Failed to open cache file at {file_path:?} using AtomicWriteFile: {err}");
139 })?;
140
141 let data = serde_json::to_string_pretty(&self).inspect_err(|err| {
142 error!("Failed to serialize cache data: {err}");
143 })?;
144 writeln!(file, "{data}")?;
145 file.commit().inspect_err(|err| {
146 error!("Failed to commit atomic write: {err}");
147 })?;
148
149 info!("Cache written to disk: {:?}", file_path);
150
151 Ok(())
152 }
153
154 pub fn cache_file_path(cache_dir: &Path, file_name: &str) -> PathBuf {
155 cache_dir
156 .join(format!("version_{}", Self::CACHE_DATA_VERSION))
157 .join(file_name)
158 }
159}
160
161impl Default for CacheData {
162 fn default() -> Self {
163 Self {
164 peers: Default::default(),
165 last_updated: SystemTime::now(),
166 network_version: crate::get_network_version(),
167 cache_version: Self::CACHE_DATA_VERSION.to_string(),
168 }
169 }
170}