use std::{collections::HashMap, fmt::Display, io::Result, time::SystemTime};
use async_trait::async_trait;
use futures::lock::Mutex;
use libp2p_identity::PeerId;
use multiaddr::Multiaddr;
use rand::{seq::IteratorRandom, thread_rng};
use crate::driver_wrapper;
#[derive(Debug, Clone)]
pub struct PeerInfo {
pub id: PeerId,
pub addrs: Vec<Multiaddr>,
pub protos: Vec<String>,
pub appear: Option<SystemTime>,
pub disappear: Option<SystemTime>,
}
impl Default for PeerInfo {
fn default() -> Self {
Self {
id: PeerId::random(),
addrs: Default::default(),
appear: Default::default(),
disappear: Default::default(),
protos: Default::default(),
}
}
}
impl PartialEq for PeerInfo {
fn eq(&self, other: &Self) -> bool {
self.id.eq(&other.id) && self.addrs.eq(&other.addrs)
}
}
impl Display for PeerInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"id={}, addrs={:?}, appear={:?}, disappear={:?}",
self.id, self.addrs, self.appear, self.disappear
)
}
}
pub mod peerbook_syscall {
use std::{io::Result, time::SystemTime};
use async_trait::async_trait;
use libp2p_identity::PeerId;
use multiaddr::Multiaddr;
use super::PeerInfo;
#[async_trait]
pub trait DriverPeerBook: Sync + Send {
async fn insert(&self, peer_info: PeerInfo) -> Result<Option<PeerInfo>>;
async fn remove(&self, peer_id: &PeerId) -> Result<Option<PeerInfo>>;
async fn appear(&self, peer_id: &PeerId, timestamp: SystemTime) -> Result<()>;
async fn disappear(&self, peer_id: &PeerId, timestamp: SystemTime) -> Result<()>;
async fn get(&self, peer_id: &PeerId) -> Result<Option<PeerInfo>>;
async fn listen_on(&self, raddr: &Multiaddr) -> Result<Option<PeerId>>;
async fn len(&self) -> usize;
async fn choose_peers(&self, protocol_id: &str, limits: usize) -> Result<Vec<PeerId>>;
async fn choose_nat_peers(&self, limits: usize) -> Result<Vec<Multiaddr>>;
}
}
driver_wrapper!(
["A type wrapper of [`DriverPeerBook`](peerbook_syscall::DriverPeerBook)"]
PeerBook[peerbook_syscall::DriverPeerBook]
);
#[derive(Default)]
struct RawMemoryPeerBook {
peer_infos: HashMap<PeerId, PeerInfo>,
peer_addrs: HashMap<Multiaddr, PeerId>,
}
#[derive(Default)]
pub struct MemoryPeerBook(Mutex<RawMemoryPeerBook>);
#[async_trait]
impl peerbook_syscall::DriverPeerBook for MemoryPeerBook {
async fn len(&self) -> usize {
self.0.lock().await.peer_infos.len()
}
async fn insert(&self, mut info: PeerInfo) -> Result<Option<PeerInfo>> {
log::trace!("MemoryPeerBook, put id={}", info.id);
info.disappear = None;
let mut raw = self.0.lock().await;
let id = info.id.clone();
let raddrs = info.addrs.clone();
let older = raw.peer_infos.insert(info.id.clone(), info);
if let Some(old) = &older {
for raddr in &old.addrs {
raw.peer_addrs.remove(raddr);
}
}
for raddr in raddrs {
raw.peer_addrs.insert(raddr, id.clone());
}
Ok(older)
}
async fn remove(&self, peer_id: &PeerId) -> Result<Option<PeerInfo>> {
let mut raw = self.0.lock().await;
let older = raw.peer_infos.remove(peer_id);
if let Some(old) = &older {
for raddr in &old.addrs {
raw.peer_addrs.remove(&raddr);
}
}
Ok(older)
}
async fn appear(&self, peer_id: &PeerId, timestamp: SystemTime) -> Result<()> {
if let Some(peer_info) = self.0.lock().await.peer_infos.get_mut(peer_id) {
peer_info.appear = Some(timestamp);
}
Ok(())
}
async fn disappear(&self, peer_id: &PeerId, timestamp: SystemTime) -> Result<()> {
if let Some(peer_info) = self.0.lock().await.peer_infos.get_mut(peer_id) {
peer_info.disappear = Some(timestamp);
}
Ok(())
}
async fn get(&self, peer_id: &PeerId) -> Result<Option<PeerInfo>> {
Ok(self
.0
.lock()
.await
.peer_infos
.get(&peer_id)
.map(|info| info.clone()))
}
async fn listen_on(&self, raddr: &Multiaddr) -> Result<Option<PeerId>> {
Ok(self
.0
.lock()
.await
.peer_addrs
.get(raddr)
.map(|id| id.clone()))
}
async fn choose_peers(&self, protocol_id: &str, maximun: usize) -> Result<Vec<PeerId>> {
let protocol_id = protocol_id.to_owned();
Ok(self
.0
.lock()
.await
.peer_infos
.iter()
.filter_map(|(_, info)| {
if info.protos.contains(&protocol_id) {
Some(info.id)
} else {
None
}
})
.choose_multiple(&mut thread_rng(), maximun))
}
async fn choose_nat_peers(&self, limits: usize) -> Result<Vec<Multiaddr>> {
use multiaddr::Protocol;
let raw = self.0.lock().await;
let circuit_suffix = Multiaddr::empty().with(Protocol::P2pCircuit);
let mut raddrs = vec![];
for (addr, id) in &raw.peer_addrs {
if addr.ends_with(&circuit_suffix) {
if let Ok(addr) = addr.clone().with_p2p(id.clone()) {
raddrs.push(addr);
if raddrs.len() == limits {
break;
}
}
}
}
Ok(raddrs)
}
}