#[macro_use]
extern crate tracing;
mod cache_store;
pub mod config;
pub mod contacts;
pub mod error;
mod initial_peers;
use ant_protocol::version::{get_network_id, get_truncate_version_str};
use libp2p::{multiaddr::Protocol, Multiaddr, PeerId};
use serde::{Deserialize, Serialize};
use std::time::SystemTime;
use thiserror::Error;
pub use cache_store::BootstrapCacheStore;
pub use config::BootstrapCacheConfig;
pub use contacts::ContactsFetcher;
pub use error::{Error, Result};
pub use initial_peers::{PeersArgs, ANT_PEERS_ENV};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BootstrapAddresses(pub Vec<BootstrapAddr>);
impl BootstrapAddresses {
pub fn insert_addr(&mut self, addr: &BootstrapAddr) {
if let Some(bootstrap_addr) = self.get_addr_mut(&addr.addr) {
bootstrap_addr.sync(addr);
} else {
self.0.push(addr.clone());
}
}
pub fn get_addr(&self, addr: &Multiaddr) -> Option<&BootstrapAddr> {
self.0
.iter()
.find(|bootstrap_addr| &bootstrap_addr.addr == addr)
}
pub fn get_addr_mut(&mut self, addr: &Multiaddr) -> Option<&mut BootstrapAddr> {
self.0
.iter_mut()
.find(|bootstrap_addr| &bootstrap_addr.addr == addr)
}
pub fn get_least_faulty(&self) -> Option<&BootstrapAddr> {
self.0.iter().min_by_key(|addr| addr.failure_rate() as u64)
}
pub fn remove_addr(&mut self, addr: &Multiaddr) {
if let Some(idx) = self
.0
.iter()
.position(|bootstrap_addr| &bootstrap_addr.addr == addr)
{
let bootstrap_addr = self.0.remove(idx);
debug!("Removed {bootstrap_addr:?}");
}
}
pub fn sync(&mut self, other: &Self) {
for other_addr in other.0.iter() {
if let Some(bootstrap_addr) = self.get_addr_mut(&other_addr.addr) {
bootstrap_addr.sync(other_addr);
} else {
trace!(
"Addr {:?} from other not found in self, inserting it.",
other_addr.addr
);
self.insert_addr(other_addr);
}
}
}
pub fn update_addr_status(&mut self, addr: &Multiaddr, success: bool) {
if let Some(bootstrap_addr) = self.get_addr_mut(addr) {
bootstrap_addr.update_status(success);
} else {
debug!("Addr not found in cache to update, skipping: {addr:?}")
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BootstrapAddr {
pub addr: Multiaddr,
pub success_count: u32,
pub failure_count: u32,
pub last_seen: SystemTime,
}
impl BootstrapAddr {
pub fn new(addr: Multiaddr) -> Self {
Self {
addr,
success_count: 0,
failure_count: 0,
last_seen: SystemTime::now(),
}
}
pub fn peer_id(&self) -> Option<PeerId> {
multiaddr_get_peer_id(&self.addr)
}
pub fn update_status(&mut self, success: bool) {
if success {
if let Some(new_value) = self.success_count.checked_add(1) {
self.success_count = new_value;
} else {
self.success_count = 1;
self.failure_count = 0;
}
}
self.last_seen = SystemTime::now();
if !success {
if let Some(new_value) = self.failure_count.checked_add(1) {
self.failure_count = new_value;
} else {
self.failure_count = 1;
self.success_count = 0;
}
}
}
pub fn is_reliable(&self) -> bool {
self.success_count >= self.failure_count
}
pub fn sync(&mut self, other: &Self) {
trace!("Syncing our state {self:?} with and other: {other:?}.");
if self.last_seen == other.last_seen {
return;
}
self.success_count = self.success_count.saturating_add(other.success_count);
self.failure_count = self.failure_count.saturating_add(other.failure_count);
if self.success_count == u32::MAX {
self.success_count = 1;
self.failure_count = 0;
} else if self.failure_count == u32::MAX {
self.failure_count = 1;
self.success_count = 0;
}
self.last_seen = std::cmp::max(self.last_seen, other.last_seen);
trace!("Successfully synced BootstrapAddr: {self:?}");
}
fn failure_rate(&self) -> f64 {
if self.success_count + self.failure_count == 0 {
0.0
} else {
self.failure_count as f64 / (self.success_count + self.failure_count) as f64
}
}
}
pub fn craft_valid_multiaddr(addr: &Multiaddr, ignore_peer_id: bool) -> Option<Multiaddr> {
let peer_id = addr
.iter()
.find(|protocol| matches!(protocol, Protocol::P2p(_)));
let mut output_address = Multiaddr::empty();
let ip = addr
.iter()
.find(|protocol| matches!(protocol, Protocol::Ip4(_)))?;
output_address.push(ip);
let udp = addr
.iter()
.find(|protocol| matches!(protocol, Protocol::Udp(_)));
let tcp = addr
.iter()
.find(|protocol| matches!(protocol, Protocol::Tcp(_)));
if let Some(udp) = udp {
output_address.push(udp);
if let Some(quic) = addr
.iter()
.find(|protocol| matches!(protocol, Protocol::QuicV1))
{
output_address.push(quic);
}
} else if let Some(tcp) = tcp {
output_address.push(tcp);
if let Some(ws) = addr
.iter()
.find(|protocol| matches!(protocol, Protocol::Ws(_)))
{
output_address.push(ws);
}
} else {
return None;
}
if let Some(peer_id) = peer_id {
output_address.push(peer_id);
} else if !ignore_peer_id {
return None;
}
Some(output_address)
}
pub fn craft_valid_multiaddr_from_str(addr_str: &str, ignore_peer_id: bool) -> Option<Multiaddr> {
let Ok(addr) = addr_str.parse::<Multiaddr>() else {
warn!("Failed to parse multiaddr from str {addr_str}");
return None;
};
craft_valid_multiaddr(&addr, ignore_peer_id)
}
pub fn multiaddr_get_peer_id(addr: &Multiaddr) -> Option<PeerId> {
match addr.iter().find(|p| matches!(p, Protocol::P2p(_))) {
Some(Protocol::P2p(id)) => Some(id),
_ => None,
}
}
pub fn get_network_version() -> String {
format!("{}_{}", get_network_id(), get_truncate_version_str())
}