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