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 info!(
114 "Found {} bootstrap addresses from environment variable",
115 bootstrap_addresses.len()
116 );
117 return Ok(bootstrap_addresses);
118 }
119
120 for addr in &self.addrs {
122 if let Some(addr) = craft_valid_multiaddr(addr, false) {
123 info!("Adding addr from arguments: {addr}");
124 bootstrap_addresses.push(BootstrapAddr::new(addr));
125 } else {
126 warn!("Invalid multiaddress format from arguments: {addr}");
127 }
128 }
129
130 if let Some(count) = count {
131 if bootstrap_addresses.len() >= count {
132 bootstrap_addresses.sort_by_key(|addr| addr.failure_rate() as u64);
133 bootstrap_addresses.truncate(count);
134 info!(
135 "Found {} bootstrap addresses. Returning early.",
136 bootstrap_addresses.len()
137 );
138 return Ok(bootstrap_addresses);
139 }
140 }
141
142 if !self.ignore_cache {
144 let cfg = if let Some(config) = config {
145 Some(config)
146 } else {
147 BootstrapCacheConfig::default_config(self.local).ok()
148 };
149 if let Some(mut cfg) = cfg {
150 if let Some(file_path) = self.get_bootstrap_cache_path()? {
151 cfg.cache_file_path = file_path;
152 }
153 info!("Loading bootstrap addresses from cache");
154 if let Ok(data) = BootstrapCacheStore::load_cache_data(&cfg) {
155 let from_cache = data.peers.into_iter().filter_map(|(_, addrs)| {
156 addrs
157 .0
158 .into_iter()
159 .min_by_key(|addr| addr.failure_rate() as u64)
160 });
161 bootstrap_addresses.extend(from_cache);
162
163 if let Some(count) = count {
164 if bootstrap_addresses.len() >= count {
165 bootstrap_addresses.sort_by_key(|addr| addr.failure_rate() as u64);
166 bootstrap_addresses.truncate(count);
167 info!(
168 "Found {} bootstrap addresses. Returning early.",
169 bootstrap_addresses.len()
170 );
171 return Ok(bootstrap_addresses);
172 }
173 }
174 }
175 }
176 } else {
177 info!("Ignoring cache, not loading bootstrap addresses from cache");
178 }
179
180 if !self.local && !self.network_contacts_url.is_empty() {
182 info!(
183 "Fetching bootstrap address from network contacts URLs: {:?}",
184 self.network_contacts_url
185 );
186 let addrs = self
187 .network_contacts_url
188 .iter()
189 .map(|url| url.parse::<Url>().map_err(|_| Error::FailedToParseUrl))
190 .collect::<Result<Vec<Url>>>()?;
191 let mut contacts_fetcher = ContactsFetcher::with_endpoints(addrs)?;
192 if let Some(count) = count {
193 contacts_fetcher.set_max_addrs(count);
194 }
195 let addrs = contacts_fetcher.fetch_bootstrap_addresses().await?;
196 bootstrap_addresses.extend(addrs);
197
198 if let Some(count) = count {
199 if bootstrap_addresses.len() >= count {
200 bootstrap_addresses.sort_by_key(|addr| addr.failure_rate() as u64);
201 bootstrap_addresses.truncate(count);
202 info!(
203 "Found {} bootstrap addresses. Returning early.",
204 bootstrap_addresses.len()
205 );
206 return Ok(bootstrap_addresses);
207 }
208 }
209 }
210
211 if !self.local && get_network_id() == MAINNET_ID {
212 let mut contacts_fetcher = ContactsFetcher::with_mainnet_endpoints()?;
213 if let Some(count) = count {
214 contacts_fetcher.set_max_addrs(count);
215 }
216 info!("Fetching bootstrap address from mainnet contacts");
217 let addrs = contacts_fetcher.fetch_bootstrap_addresses().await?;
218 bootstrap_addresses.extend(addrs);
219 } else if !self.local && get_network_id() == ALPHANET_ID {
220 let mut contacts_fetcher = ContactsFetcher::with_alphanet_endpoints()?;
221 if let Some(count) = count {
222 contacts_fetcher.set_max_addrs(count);
223 }
224 info!("Fetching bootstrap address from alphanet contacts");
225 let addrs = contacts_fetcher.fetch_bootstrap_addresses().await?;
226 bootstrap_addresses.extend(addrs);
227 }
228
229 if !bootstrap_addresses.is_empty() {
230 bootstrap_addresses.sort_by_key(|addr| addr.failure_rate() as u64);
231 if let Some(count) = count {
232 bootstrap_addresses.truncate(count);
233 }
234 info!(
235 "Found {} bootstrap addresses. Returning early.",
236 bootstrap_addresses.len()
237 );
238 Ok(bootstrap_addresses)
239 } else {
240 error!("No initial bootstrap peers found through any means");
241 Err(Error::NoBootstrapPeersFound)
242 }
243 }
244
245 pub fn read_addr_from_env() -> Vec<Multiaddr> {
246 Self::read_bootstrap_addr_from_env()
247 .into_iter()
248 .map(|addr| addr.addr)
249 .collect()
250 }
251
252 pub fn read_bootstrap_addr_from_env() -> Vec<BootstrapAddr> {
253 let mut bootstrap_addresses = Vec::new();
254 if let Ok(addrs) = std::env::var(ANT_PEERS_ENV) {
256 for addr_str in addrs.split(',') {
257 if let Some(addr) = craft_valid_multiaddr_from_str(addr_str, false) {
258 info!("Adding addr from environment variable: {addr}");
259 bootstrap_addresses.push(BootstrapAddr::new(addr));
260 } else {
261 warn!("Invalid multiaddress format from environment variable: {addr_str}");
262 }
263 }
264 }
265 bootstrap_addresses
266 }
267
268 pub fn get_bootstrap_cache_path(&self) -> Result<Option<PathBuf>> {
270 if let Some(dir) = &self.bootstrap_cache_dir {
271 if dir.is_file() {
272 return Err(Error::InvalidBootstrapCacheDir);
273 }
274
275 if !dir.exists() {
276 std::fs::create_dir_all(dir)?;
277 }
278
279 let path = dir.join(cache_file_name());
280 Ok(Some(path))
281 } else {
282 Ok(None)
283 }
284 }
285}