ant_bootstrap/
initial_peers.rs1use crate::{
10 config::cache_file_name,
11 craft_valid_multiaddr, craft_valid_multiaddr_from_str,
12 error::{Error, Result},
13 BootstrapAddr, BootstrapCacheConfig, BootstrapCacheStore, ContactsFetcher,
14};
15use clap::Args;
16use libp2p::Multiaddr;
17use serde::{Deserialize, Serialize};
18use std::path::PathBuf;
19use url::Url;
20
21pub const ANT_PEERS_ENV: &str = "ANT_PEERS";
23
24#[derive(Args, Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27pub struct InitialPeersConfig {
28 #[clap(long, default_value = "false")]
33 pub first: bool,
34 #[clap(
45 long = "peer",
46 value_name = "multiaddr",
47 value_delimiter = ',',
48 conflicts_with = "first"
49 )]
50 pub addrs: Vec<Multiaddr>,
51 #[clap(long, conflicts_with = "first", value_delimiter = ',')]
56 pub network_contacts_url: Vec<String>,
57 #[clap(long, conflicts_with = "network_contacts_url", default_value = "false")]
59 pub local: bool,
60 #[clap(name = "testnet", long)]
64 pub disable_mainnet_contacts: bool,
65 #[clap(long, default_value = "false")]
67 pub ignore_cache: bool,
68 #[clap(long)]
77 pub bootstrap_cache_dir: Option<PathBuf>,
78}
79
80impl InitialPeersConfig {
81 pub async fn get_addrs(
84 &self,
85 config: Option<BootstrapCacheConfig>,
86 count: Option<usize>,
87 ) -> Result<Vec<Multiaddr>> {
88 Ok(self
89 .get_bootstrap_addr(config, count)
90 .await?
91 .into_iter()
92 .map(|addr| addr.addr)
93 .collect())
94 }
95
96 pub async fn get_bootstrap_addr(
99 &self,
100 config: Option<BootstrapCacheConfig>,
101 count: Option<usize>,
102 ) -> Result<Vec<BootstrapAddr>> {
103 if self.first {
105 info!("First node in network, no initial bootstrap peers");
106 return Ok(vec![]);
107 }
108
109 let mut bootstrap_addresses = vec![];
110
111 bootstrap_addresses.extend(Self::read_bootstrap_addr_from_env());
113
114 if !bootstrap_addresses.is_empty() {
115 return Ok(bootstrap_addresses);
116 }
117
118 for addr in &self.addrs {
120 if let Some(addr) = craft_valid_multiaddr(addr, false) {
121 info!("Adding addr from arguments: {addr}");
122 bootstrap_addresses.push(BootstrapAddr::new(addr));
123 } else {
124 warn!("Invalid multiaddress format from arguments: {addr}");
125 }
126 }
127
128 if let Some(count) = count {
129 if bootstrap_addresses.len() >= count {
130 bootstrap_addresses.sort_by_key(|addr| addr.failure_rate() as u64);
131 bootstrap_addresses.truncate(count);
132 info!("Returning early as enough bootstrap addresses are found");
133 return Ok(bootstrap_addresses);
134 }
135 }
136
137 if !self.ignore_cache {
139 let cfg = if let Some(config) = config {
140 Some(config)
141 } else {
142 BootstrapCacheConfig::default_config(self.local).ok()
143 };
144 if let Some(mut cfg) = cfg {
145 if let Some(file_path) = self.get_bootstrap_cache_path()? {
146 cfg.cache_file_path = file_path;
147 }
148 info!("Loading bootstrap addresses from cache");
149 if let Ok(data) = BootstrapCacheStore::load_cache_data(&cfg) {
150 let from_cache = data.peers.into_iter().filter_map(|(_, addrs)| {
151 addrs
152 .0
153 .into_iter()
154 .min_by_key(|addr| addr.failure_rate() as u64)
155 });
156 bootstrap_addresses.extend(from_cache);
157
158 if let Some(count) = count {
159 if bootstrap_addresses.len() >= count {
160 bootstrap_addresses.sort_by_key(|addr| addr.failure_rate() as u64);
161 bootstrap_addresses.truncate(count);
162 info!("Returning early as enough bootstrap addresses are found");
163 return Ok(bootstrap_addresses);
164 }
165 }
166 }
167 }
168 } else {
169 info!("Ignoring cache, not loading bootstrap addresses from cache");
170 }
171
172 if !self.local && !self.network_contacts_url.is_empty() {
174 info!(
175 "Fetching bootstrap address from network contacts URLs: {:?}",
176 self.network_contacts_url
177 );
178 let addrs = self
179 .network_contacts_url
180 .iter()
181 .map(|url| url.parse::<Url>().map_err(|_| Error::FailedToParseUrl))
182 .collect::<Result<Vec<Url>>>()?;
183 let mut contacts_fetcher = ContactsFetcher::with_endpoints(addrs)?;
184 if let Some(count) = count {
185 contacts_fetcher.set_max_addrs(count);
186 }
187 let addrs = contacts_fetcher.fetch_bootstrap_addresses().await?;
188 bootstrap_addresses.extend(addrs);
189
190 if let Some(count) = count {
191 if bootstrap_addresses.len() >= count {
192 bootstrap_addresses.sort_by_key(|addr| addr.failure_rate() as u64);
193 bootstrap_addresses.truncate(count);
194 info!("Returning early as enough bootstrap addresses are found");
195 return Ok(bootstrap_addresses);
196 }
197 }
198 }
199
200 if !self.local && !self.disable_mainnet_contacts {
201 let mut contacts_fetcher = ContactsFetcher::with_mainnet_endpoints()?;
202 if let Some(count) = count {
203 contacts_fetcher.set_max_addrs(count);
204 }
205 let addrs = contacts_fetcher.fetch_bootstrap_addresses().await?;
206 bootstrap_addresses.extend(addrs);
207 }
208
209 if !bootstrap_addresses.is_empty() {
210 bootstrap_addresses.sort_by_key(|addr| addr.failure_rate() as u64);
211 if let Some(count) = count {
212 bootstrap_addresses.truncate(count);
213 }
214 Ok(bootstrap_addresses)
215 } else {
216 error!("No initial bootstrap peers found through any means");
217 Err(Error::NoBootstrapPeersFound)
218 }
219 }
220
221 pub fn read_addr_from_env() -> Vec<Multiaddr> {
222 Self::read_bootstrap_addr_from_env()
223 .into_iter()
224 .map(|addr| addr.addr)
225 .collect()
226 }
227
228 pub fn read_bootstrap_addr_from_env() -> Vec<BootstrapAddr> {
229 let mut bootstrap_addresses = Vec::new();
230 if let Ok(addrs) = std::env::var(ANT_PEERS_ENV) {
232 for addr_str in addrs.split(',') {
233 if let Some(addr) = craft_valid_multiaddr_from_str(addr_str, false) {
234 info!("Adding addr from environment variable: {addr}");
235 bootstrap_addresses.push(BootstrapAddr::new(addr));
236 } else {
237 warn!("Invalid multiaddress format from environment variable: {addr_str}");
238 }
239 }
240 }
241 bootstrap_addresses
242 }
243
244 pub fn get_bootstrap_cache_path(&self) -> Result<Option<PathBuf>> {
246 if let Some(dir) = &self.bootstrap_cache_dir {
247 if dir.is_file() {
248 return Err(Error::InvalidBootstrapCacheDir);
249 }
250
251 if !dir.exists() {
252 std::fs::create_dir_all(dir)?;
253 }
254
255 let path = dir.join(cache_file_name());
256 Ok(Some(path))
257 } else {
258 Ok(None)
259 }
260 }
261}