ant_bootstrap/cache_store/
mod.rs1pub mod cache_data_v0;
10pub mod cache_data_v1;
11
12use crate::{craft_valid_multiaddr, BootstrapCacheConfig, Error, InitialPeersConfig, Result};
13use libp2p::{multiaddr::Protocol, Multiaddr, PeerId};
14use std::fs;
15
16pub type CacheDataLatest = cache_data_v1::CacheData;
17pub const CACHE_DATA_VERSION_LATEST: u32 = cache_data_v1::CacheData::CACHE_DATA_VERSION;
18
19#[derive(Clone, Debug)]
20pub struct BootstrapCacheStore {
21 pub(crate) config: BootstrapCacheConfig,
22 pub(crate) data: CacheDataLatest,
23}
24
25impl BootstrapCacheStore {
26 pub fn config(&self) -> &BootstrapCacheConfig {
27 &self.config
28 }
29
30 pub fn new(config: BootstrapCacheConfig) -> Result<Self> {
32 info!("Creating new CacheStore with config: {:?}", config);
33
34 if !config.cache_dir.exists() {
36 info!(
37 "Attempting to create cache directory at {:?}",
38 config.cache_dir
39 );
40 fs::create_dir_all(&config.cache_dir).inspect_err(|err| {
41 warn!(
42 "Failed to create cache directory at {:?}: {err}",
43 config.cache_dir
44 );
45 })?;
46 }
47
48 let store = Self {
49 config,
50 data: CacheDataLatest::default(),
51 };
52
53 Ok(store)
54 }
55
56 pub fn new_from_initial_peers_config(
62 init_peers_config: &InitialPeersConfig,
63 config: Option<BootstrapCacheConfig>,
64 ) -> Result<Self> {
65 let mut config = if let Some(cfg) = config {
66 cfg
67 } else {
68 BootstrapCacheConfig::new(init_peers_config.local)?
69 };
70
71 if let Some(cache_dir) = &init_peers_config.bootstrap_cache_dir {
72 config.cache_dir = cache_dir.clone();
73 }
74
75 let mut store = Self::new(config)?;
76
77 if init_peers_config.first {
79 info!("First node in network, writing empty cache to disk");
80 store.write()?;
81 } else {
82 info!("Flushing cache to disk on init.");
83 store.sync_and_flush_to_disk()?;
84 }
85
86 Ok(store)
87 }
88
89 pub fn peer_count(&self) -> usize {
90 self.data.peers.len()
91 }
92
93 pub fn get_all_addrs(&self) -> impl Iterator<Item = &Multiaddr> {
94 self.data.get_all_addrs()
95 }
96
97 pub fn remove_peer(&mut self, peer_id: &PeerId) {
99 self.data.peers.retain(|(id, _)| id != peer_id);
100 }
101
102 pub fn add_addr(&mut self, addr: Multiaddr) {
104 let Some(addr) = craft_valid_multiaddr(&addr, false) else {
105 return;
106 };
107 let peer_id = match addr.iter().find(|p| matches!(p, Protocol::P2p(_))) {
108 Some(Protocol::P2p(id)) => id,
109 _ => return,
110 };
111 if addr.iter().any(|p| matches!(p, Protocol::P2pCircuit)) {
112 return;
113 }
114
115 debug!("Adding addr to bootstrap cache: {addr}");
116
117 self.data.add_peer(
118 peer_id,
119 [addr].iter(),
120 self.config.max_addrs_per_peer,
121 self.config.max_peers,
122 );
123 }
124
125 pub fn load_cache_data(cfg: &BootstrapCacheConfig) -> Result<CacheDataLatest> {
128 match cache_data_v1::CacheData::read_from_file(
130 &cfg.cache_dir,
131 &Self::cache_file_name(cfg.local),
132 ) {
133 Ok(mut data) => {
134 while data.peers.len() > cfg.max_peers {
135 data.peers.pop_front();
136 }
137 return Ok(data);
138 }
139 Err(err) => {
140 warn!("Failed to load cache data from latest version: {err}");
141 }
142 }
143
144 match cache_data_v0::CacheData::read_from_file(
146 &cfg.cache_dir,
147 &Self::cache_file_name(cfg.local),
148 ) {
149 Ok(data) => {
150 warn!("Loaded cache data from older version, upgrading to latest version");
151 let mut data: CacheDataLatest = data.into();
152 while data.peers.len() > cfg.max_peers {
153 data.peers.pop_front();
154 }
155
156 Ok(data)
157 }
158 Err(err) => {
159 warn!("Failed to load cache data from older version: {err}");
160 Err(Error::FailedToParseCacheData)
161 }
162 }
163 }
164
165 pub fn sync_and_flush_to_disk(&mut self) -> Result<()> {
168 if self.config.disable_cache_writing {
169 info!("Cache writing is disabled, skipping sync to disk");
170 return Ok(());
171 }
172
173 info!(
174 "Flushing cache to disk, with data containing: {} peers",
175 self.data.peers.len(),
176 );
177
178 if let Ok(data_from_file) = Self::load_cache_data(&self.config) {
179 self.data.sync(
180 &data_from_file,
181 self.config.max_addrs_per_peer,
182 self.config.max_peers,
183 );
184 } else {
185 warn!("Failed to load cache data from file, overwriting with new data");
186 }
187
188 self.write().inspect_err(|e| {
189 error!("Failed to save cache to disk: {e}");
190 })?;
191
192 self.data.peers.clear();
194
195 Ok(())
196 }
197
198 pub fn write(&self) -> Result<()> {
201 if self.config.disable_cache_writing {
202 info!("Cache writing is disabled, skipping sync to disk");
203 return Ok(());
204 }
205
206 let filename = Self::cache_file_name(self.config.local);
207
208 self.data.write_to_file(&self.config.cache_dir, &filename)?;
209
210 if self.config.backwards_compatible_writes {
211 cache_data_v0::CacheData::from(&self.data)
212 .write_to_file(&self.config.cache_dir, &filename)?;
213 }
214
215 Ok(())
216 }
217
218 pub fn cache_file_name(local: bool) -> String {
220 if local {
221 format!(
222 "bootstrap_cache_local_{}.json",
223 crate::get_network_version()
224 )
225 } else {
226 format!("bootstrap_cache_{}.json", crate::get_network_version())
227 }
228 }
229}