use crate::error::{Error, Result};
use clap::Args;
use libp2p::Multiaddr;
use serde::{Deserialize, Serialize};
use std::{
env,
path::{Path, PathBuf},
time::Duration,
};
const MAX_CACHED_PEERS: usize = 1500;
const MAX_ADDRS_PER_CACHED_PEER: usize = 3;
const MIN_BOOTSTRAP_CACHE_SAVE_INTERVAL: Duration = Duration::from_secs(30);
const MAX_BOOTSTRAP_CACHE_SAVE_INTERVAL: Duration = Duration::from_secs(3 * 60 * 60);
const CONCURRENT_DIALS: usize = 10;
const MAX_PEERS_BEFORE_TERMINATION: usize = 5;
#[derive(Args, Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct InitialPeersConfig {
#[clap(long, default_value = "false")]
pub first: bool,
#[clap(
long = "peer",
value_name = "multiaddr",
value_delimiter = ',',
conflicts_with = "first"
)]
pub addrs: Vec<Multiaddr>,
#[clap(long, conflicts_with = "first", value_delimiter = ',')]
pub network_contacts_url: Vec<String>,
#[clap(long, conflicts_with = "network_contacts_url", default_value = "false")]
pub local: bool,
#[clap(long, default_value = "false")]
pub ignore_cache: bool,
#[clap(long)]
pub bootstrap_cache_dir: Option<PathBuf>,
}
#[derive(Clone, Debug)]
pub struct BootstrapConfig {
pub backwards_compatible_writes: bool,
pub cache_dir: PathBuf,
pub cache_save_scaling_factor: u32,
pub disable_cache_writing: bool,
pub disable_cache_reading: bool,
pub disable_env_peers: bool,
pub first: bool,
pub initial_peers: Vec<Multiaddr>,
pub local: bool,
pub max_cache_save_duration: Duration,
pub max_concurrent_dials: usize,
pub max_contacted_peers_before_termination: usize,
pub max_cached_peers: usize,
pub max_addrs_per_cached_peer: usize,
pub min_cache_save_duration: Duration,
pub network_contacts_url: Vec<String>,
}
impl Default for BootstrapConfig {
fn default() -> Self {
Self {
backwards_compatible_writes: false,
cache_dir: default_cache_dir(),
cache_save_scaling_factor: 2,
disable_cache_writing: false,
disable_cache_reading: false,
disable_env_peers: false,
first: false,
initial_peers: vec![],
local: false,
max_concurrent_dials: CONCURRENT_DIALS,
max_contacted_peers_before_termination: MAX_PEERS_BEFORE_TERMINATION,
max_cached_peers: MAX_CACHED_PEERS,
max_addrs_per_cached_peer: MAX_ADDRS_PER_CACHED_PEER,
min_cache_save_duration: MIN_BOOTSTRAP_CACHE_SAVE_INTERVAL,
max_cache_save_duration: MAX_BOOTSTRAP_CACHE_SAVE_INTERVAL,
network_contacts_url: vec![],
}
}
}
impl TryFrom<&InitialPeersConfig> for BootstrapConfig {
type Error = Error;
fn try_from(config: &InitialPeersConfig) -> Result<Self> {
let cache_dir = if let Some(cache_dir) = &config.bootstrap_cache_dir {
cache_dir.clone()
} else {
default_cache_dir()
};
let bootstrap_config = Self {
cache_dir,
disable_cache_reading: config.ignore_cache,
first: config.first,
initial_peers: config.addrs.clone(),
local: config.local,
network_contacts_url: config.network_contacts_url.clone(),
..Self::default()
};
Ok(bootstrap_config)
}
}
impl BootstrapConfig {
pub fn new(local: bool) -> Self {
Self {
local,
cache_dir: default_cache_dir(),
..Self::default()
}
}
pub fn with_backwards_compatible_writes(mut self, enable: bool) -> Self {
self.backwards_compatible_writes = enable;
self
}
pub fn with_local(mut self, enable: bool) -> Self {
self.local = enable;
self
}
pub fn with_cache_dir<P: AsRef<Path>>(mut self, path: P) -> Self {
self.cache_dir = path.as_ref().to_path_buf();
self
}
pub fn with_max_concurrent_dials(mut self, max_dials: usize) -> Self {
self.max_concurrent_dials = max_dials;
self
}
pub fn with_max_contacted_peers_before_termination(mut self, max_peers: usize) -> Self {
self.max_contacted_peers_before_termination = max_peers;
self
}
pub fn with_max_cached_peers(mut self, max_peers: usize) -> Self {
self.max_cached_peers = max_peers;
self
}
pub fn with_max_addrs_per_cached_peer(mut self, max_addrs: usize) -> Self {
self.max_addrs_per_cached_peer = max_addrs;
self
}
pub fn with_disable_cache_writing(mut self, disable: bool) -> Self {
self.disable_cache_writing = disable;
self
}
pub fn with_disable_cache_reading(mut self, disable: bool) -> Self {
self.disable_cache_reading = disable;
self
}
pub fn with_disable_env_peers(mut self, disable: bool) -> Self {
self.disable_env_peers = disable;
self
}
pub fn with_first(mut self, first: bool) -> Self {
self.first = first;
self
}
pub fn with_initial_peers(mut self, peers: Vec<Multiaddr>) -> Self {
self.initial_peers = peers;
self
}
pub fn with_cache_save_scaling_factor(mut self, factor: u32) -> Self {
self.cache_save_scaling_factor = factor;
self
}
pub fn with_max_cache_save_duration(mut self, duration: Duration) -> Self {
self.max_cache_save_duration = duration;
self
}
pub fn with_min_cache_save_duration(mut self, duration: Duration) -> Self {
self.min_cache_save_duration = duration;
self
}
pub fn with_network_contacts_url(mut self, urls: Vec<String>) -> Self {
self.network_contacts_url = urls;
self
}
}
fn default_cache_dir() -> PathBuf {
let base_dir = if let Some(dir) = dirs_next::data_dir() {
dir
} else if let Some(home) = dirs_next::home_dir() {
warn!("Failed to obtain platform data directory, falling back to home directory");
home
} else {
let cwd = env::current_dir().unwrap_or_else(|err| {
error!("Failed to obtain current working directory: {err}. Using current process directory '.'");
PathBuf::from(".")
});
warn!("Falling back to current working directory for bootstrap cache");
cwd
};
base_dir.join("autonomi").join("bootstrap_cache")
}