ant_bootstrap/config.rs
1// Copyright 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use crate::error::{Error, Result};
10use clap::Args;
11use libp2p::Multiaddr;
12use serde::{Deserialize, Serialize};
13use std::{
14 env,
15 path::{Path, PathBuf},
16 time::Duration,
17};
18
19/// Maximum peers to store
20const MAX_CACHED_PEERS: usize = 1500;
21
22/// Maximum number of addresses to store for a Peer
23const MAX_ADDRS_PER_CACHED_PEER: usize = 3;
24
25// Min time until we save the bootstrap cache to disk. 30 secs
26const MIN_BOOTSTRAP_CACHE_SAVE_INTERVAL: Duration = Duration::from_secs(30);
27
28// Max time until we save the bootstrap cache to disk. 3 hours
29const MAX_BOOTSTRAP_CACHE_SAVE_INTERVAL: Duration = Duration::from_secs(3 * 60 * 60);
30
31/// The max number of concurrent dials to be made during the initial bootstrap process.
32const CONCURRENT_DIALS: usize = 5;
33
34/// The max number of peers to be added before stopping the initial bootstrap process.
35const MAX_PEERS_BEFORE_TERMINATION: usize = 5;
36
37/// Configurations to fetch the initial peers which is used to bootstrap the network.
38/// This could optionally also be used as a command line argument struct.
39#[derive(Args, Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
40pub struct InitialPeersConfig {
41 /// Set to indicate this is the first node in a new network
42 ///
43 /// If this argument is used, any others will be ignored because they do not apply to the first
44 /// node.
45 #[clap(long, default_value = "false")]
46 pub first: bool,
47 /// Addr(s) to use for bootstrap, in a 'multiaddr' format containing the peer ID.
48 ///
49 /// A multiaddr looks like
50 /// '/ip4/1.2.3.4/tcp/1200/tcp/p2p/12D3KooWRi6wF7yxWLuPSNskXc6kQ5cJ6eaymeMbCRdTnMesPgFx' where
51 /// `1.2.3.4` is the IP, `1200` is the port and the (optional) last part is the peer ID.
52 ///
53 /// This argument can be provided multiple times to connect to multiple peers.
54 ///
55 /// Alternatively, the `ANT_PEERS` environment variable can provide a comma-separated peer
56 /// list.
57 #[clap(
58 long = "peer",
59 value_name = "multiaddr",
60 value_delimiter = ',',
61 conflicts_with = "first"
62 )]
63 pub addrs: Vec<Multiaddr>,
64 /// Specify the URL to fetch the network contacts from.
65 ///
66 /// The URL can point to a text file containing Multiaddresses separated by newline character, or
67 /// a bootstrap cache JSON file.
68 #[clap(long, conflicts_with = "first", value_delimiter = ',')]
69 pub network_contacts_url: Vec<String>,
70 /// Set to indicate this is a local network.
71 #[clap(long, conflicts_with = "network_contacts_url", default_value = "false")]
72 pub local: bool,
73 /// Set to not load the bootstrap addresses from the local cache.
74 #[clap(long, default_value = "false")]
75 pub ignore_cache: bool,
76 /// The directory to load and store the bootstrap cache. If not provided, the default path will be used.
77 ///
78 /// The JSON filename will be derived automatically from the network ID
79 ///
80 /// The default location is platform specific:
81 /// - Linux: $HOME/.local/share/autonomi/bootstrap_cache/bootstrap_cache_<network_id>.json
82 /// - macOS: $HOME/Library/Application Support/autonomi/bootstrap_cache/bootstrap_cache_<network_id>.json
83 /// - Windows: C:\Users\<username>\AppData\Roaming\autonomi\bootstrap_cache\bootstrap_cache_<network_id>.json
84 ///
85 /// We fallback to $HOME dir and then to current working directory if the platform specific directory cannot be
86 /// determined.
87 #[clap(long)]
88 pub bootstrap_cache_dir: Option<PathBuf>,
89}
90
91/// Configuration for Bootstrapping
92///
93/// If you have `InitialPeersConfig`, you can convert it to `BootstrapConfig` using `TryFrom` trait.
94#[derive(Clone, Debug)]
95pub struct BootstrapConfig {
96 /// Enable backwards compatibility while writing the cache file.
97 /// This will write the cache file in all versions of the cache file format.
98 pub backwards_compatible_writes: bool,
99 /// The directory to load and store the bootstrap cache. If not provided, the default path will be used.
100 ///
101 /// The JSON filename will be derived automatically from the network ID
102 ///
103 /// The default location is platform specific:
104 /// - Linux: $HOME/.local/share/autonomi/bootstrap_cache/bootstrap_cache_<network_id>.json
105 /// - macOS: $HOME/Library/Application Support/autonomi/bootstrap_cache/bootstrap_cache_<network_id>.json
106 /// - Windows: C:\Users\<username>\AppData\Roaming\autonomi\bootstrap_cache\bootstrap_cache_<network_id>.json
107 pub cache_dir: PathBuf,
108 /// The cache save scaling factor. We start with the min_cache_save_duration and scale it up to the max_cache_save_duration.
109 pub cache_save_scaling_factor: u32,
110 /// Flag to disable writing to the cache file
111 pub disable_cache_writing: bool,
112 /// Flag to disable reading from the cache file
113 pub disable_cache_reading: bool,
114 /// Flag to disable reading peers from the ANT_PEERS environment variable
115 pub disable_env_peers: bool,
116 /// Indicate that this is the first node in a new network.
117 pub first: bool,
118 /// The initial peers that are used to bootstrap/connect the network.
119 pub initial_peers: Vec<Multiaddr>,
120 /// If set to true, the cache filename will be suffixed with "_local"
121 pub local: bool,
122 /// The max time duration until we save the bootstrap cache to disk.
123 pub max_cache_save_duration: Duration,
124 /// The max number of concurrent dials to be made during the initial bootstrap process.
125 ///
126 /// This is the number of peers we will try to dial in parallel.
127 /// Default is 5.
128 pub max_concurrent_dials: usize,
129 /// The max number of peers to be added to RT / connected before stopping the initial bootstrap process.
130 /// Default is 5.
131 pub max_contacted_peers_before_termination: usize,
132 /// Maximum number of peers to store inside the bootstrap cache
133 ///
134 /// When the number of cached peers exceeds this value, the least recently seen peers will be removed.
135 /// Default is 1500.
136 pub max_cached_peers: usize,
137 /// Maximum number of addresses stored per peer inside the bootstrap cache.
138 /// Default is 3.
139 pub max_addrs_per_cached_peer: usize,
140 /// The min time duration until we save the bootstrap cache to disk.
141 pub min_cache_save_duration: Duration,
142 /// Specify the URL to fetch the network contacts from.
143 ///
144 /// The URL can point to a text file containing Multiaddresses separated by newline character, or
145 /// a bootstrap cache JSON file.
146 pub network_contacts_url: Vec<String>,
147}
148
149impl Default for BootstrapConfig {
150 fn default() -> Self {
151 Self {
152 backwards_compatible_writes: false,
153 cache_dir: default_cache_dir(),
154 cache_save_scaling_factor: 2,
155 disable_cache_writing: false,
156 disable_cache_reading: false,
157 disable_env_peers: false,
158 first: false,
159 initial_peers: vec![],
160 local: false,
161 max_concurrent_dials: CONCURRENT_DIALS,
162 max_contacted_peers_before_termination: MAX_PEERS_BEFORE_TERMINATION,
163 max_cached_peers: MAX_CACHED_PEERS,
164 max_addrs_per_cached_peer: MAX_ADDRS_PER_CACHED_PEER,
165 min_cache_save_duration: MIN_BOOTSTRAP_CACHE_SAVE_INTERVAL,
166 max_cache_save_duration: MAX_BOOTSTRAP_CACHE_SAVE_INTERVAL,
167 network_contacts_url: vec![],
168 }
169 }
170}
171
172impl TryFrom<&InitialPeersConfig> for BootstrapConfig {
173 type Error = Error;
174 fn try_from(config: &InitialPeersConfig) -> Result<Self> {
175 let cache_dir = if let Some(cache_dir) = &config.bootstrap_cache_dir {
176 cache_dir.clone()
177 } else {
178 default_cache_dir()
179 };
180
181 let bootstrap_config = Self {
182 cache_dir,
183 disable_cache_reading: config.ignore_cache,
184 first: config.first,
185 initial_peers: config.addrs.clone(),
186 local: config.local,
187 network_contacts_url: config.network_contacts_url.clone(),
188 ..Self::default()
189 };
190 Ok(bootstrap_config)
191 }
192}
193
194impl BootstrapConfig {
195 /// Creates a new BootstrapConfig with default settings
196 pub fn new(local: bool) -> Self {
197 Self {
198 local,
199 cache_dir: default_cache_dir(),
200 ..Self::default()
201 }
202 }
203
204 /// Set backwards compatible writes
205 pub fn with_backwards_compatible_writes(mut self, enable: bool) -> Self {
206 self.backwards_compatible_writes = enable;
207 self
208 }
209
210 /// Set the local flag
211 pub fn with_local(mut self, enable: bool) -> Self {
212 self.local = enable;
213 self
214 }
215
216 /// Update the config with a custom cache file path
217 pub fn with_cache_dir<P: AsRef<Path>>(mut self, path: P) -> Self {
218 self.cache_dir = path.as_ref().to_path_buf();
219 self
220 }
221
222 /// Sets the maximum number of concurrent dials to be made during the initial bootstrap process
223 /// Default is 5.
224 pub fn with_max_concurrent_dials(mut self, max_dials: usize) -> Self {
225 self.max_concurrent_dials = max_dials;
226 self
227 }
228
229 /// Sets the maximum number of peers to be added / contacted before stopping the initial bootstrap process
230 /// Default is 5.
231 pub fn with_max_contacted_peers_before_termination(mut self, max_peers: usize) -> Self {
232 self.max_contacted_peers_before_termination = max_peers;
233 self
234 }
235
236 /// Sets the maximum number of cached peers
237 pub fn with_max_cached_peers(mut self, max_peers: usize) -> Self {
238 self.max_cached_peers = max_peers;
239 self
240 }
241
242 /// Sets the maximum number of addresses for a single peer in the bootstrap cache
243 pub fn with_max_addrs_per_cached_peer(mut self, max_addrs: usize) -> Self {
244 self.max_addrs_per_cached_peer = max_addrs;
245 self
246 }
247
248 /// Sets the flag to disable writing to the cache file
249 pub fn with_disable_cache_writing(mut self, disable: bool) -> Self {
250 self.disable_cache_writing = disable;
251 self
252 }
253
254 /// Sets the flag to disable reading from the cache file
255 pub fn with_disable_cache_reading(mut self, disable: bool) -> Self {
256 self.disable_cache_reading = disable;
257 self
258 }
259
260 /// Sets the flag to disable reading peers from the ANT_PEERS environment variable
261 pub fn with_disable_env_peers(mut self, disable: bool) -> Self {
262 self.disable_env_peers = disable;
263 self
264 }
265
266 /// Sets whether this config represents the first node in the network
267 pub fn with_first(mut self, first: bool) -> Self {
268 self.first = first;
269 self
270 }
271
272 /// Sets the initial peers that should be used for bootstrapping
273 pub fn with_initial_peers(mut self, peers: Vec<Multiaddr>) -> Self {
274 self.initial_peers = peers;
275 self
276 }
277
278 /// Sets the cache save scaling factor
279 pub fn with_cache_save_scaling_factor(mut self, factor: u32) -> Self {
280 self.cache_save_scaling_factor = factor;
281 self
282 }
283
284 /// Sets the maximum duration between cache saves
285 pub fn with_max_cache_save_duration(mut self, duration: Duration) -> Self {
286 self.max_cache_save_duration = duration;
287 self
288 }
289
290 /// Sets the minimum duration between cache saves
291 pub fn with_min_cache_save_duration(mut self, duration: Duration) -> Self {
292 self.min_cache_save_duration = duration;
293 self
294 }
295
296 /// Sets the list of network contact URLs
297 pub fn with_network_contacts_url(mut self, urls: Vec<String>) -> Self {
298 self.network_contacts_url = urls;
299 self
300 }
301}
302
303/// Returns the default dir that should contain the bootstrap cache file
304///
305/// The default location is platform specific:
306/// - Linux: $HOME/.local/share/autonomi/bootstrap_cache/bootstrap_cache_<network_id>.json
307/// - macOS: $HOME/Library/Application Support/autonomi/bootstrap_cache/bootstrap_cache_<network_id>.json
308/// - Windows: C:\Users\<username>\AppData\Roaming\autonomi\bootstrap_cache\bootstrap_cache_<network_id>.json
309///
310/// We fallback to $HOME dir and then to current working directory if the platform specific directory cannot be
311/// determined.
312fn default_cache_dir() -> PathBuf {
313 let base_dir = if let Some(dir) = dirs_next::data_dir() {
314 dir
315 } else if let Some(home) = dirs_next::home_dir() {
316 warn!("Failed to obtain platform data directory, falling back to home directory");
317 home
318 } else {
319 let cwd = env::current_dir().unwrap_or_else(|err| {
320 error!("Failed to obtain current working directory: {err}. Using current process directory '.'");
321 PathBuf::from(".")
322 });
323 warn!("Falling back to current working directory for bootstrap cache");
324 cwd
325 };
326
327 base_dir.join("autonomi").join("bootstrap_cache")
328}