#[cfg(feature = "mdns")]
use libp2p::mdns::{MdnsConfig, TokioMdns};
use libp2p::{
gossipsub::{
Gossipsub, GossipsubConfigBuilder, GossipsubMessage, MessageAuthenticity, MessageId,
ValidationMode,
},
identity::Keypair,
kad::{store::MemoryStore, Kademlia, KademliaConfig},
Multiaddr, NetworkBehaviour, PeerId,
};
use std::hash::Hash;
use std::hash::Hasher;
use std::{collections::hash_map::DefaultHasher, str::FromStr, time::Duration};
use super::event::SatelliteSwamEvent;
use crate::{errors::TatamiError, Satellite};
const BOOTNODES: [&str; 4] = [
"QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
"QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
"QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
"QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
];
#[derive(NetworkBehaviour)]
#[behaviour(out_event = "SatelliteSwamEvent")]
pub struct SwarmBehaviour {
pub kademlia: Kademlia<MemoryStore>,
#[cfg(feature = "mdns")]
pub mdns: TokioMdns,
pub gossipsub: Gossipsub,
}
impl SwarmBehaviour {
pub async fn new(local_key: Keypair, bootstrap: bool) -> Result<Self, TatamiError> {
let local_public_key = &local_key.public();
let local_peer_id = PeerId::from_public_key(&local_public_key);
Ok(Self {
kademlia: Self::kad(local_peer_id, bootstrap)?,
#[cfg(feature = "mdns")]
mdns: Self::mdns().await?,
gossipsub: Self::gossipsub(local_key)?,
})
}
#[cfg(feature = "mdns")]
async fn mdns() -> Result<TokioMdns, TatamiError> {
TokioMdns::new(MdnsConfig::default())
.await
.or(Err(TatamiError::Generic))
}
fn kad(local_peer_id: PeerId, bootstrap: bool) -> Result<Kademlia<MemoryStore>, TatamiError> {
let store = MemoryStore::new(local_peer_id);
let mut cfg = KademliaConfig::default();
cfg.set_query_timeout(Duration::from_secs(5 * 60));
let mut kad = Kademlia::with_config(local_peer_id, store, cfg);
if bootstrap {
let bootaddr = Multiaddr::from_str("/dnsaddr/bootstrap.libp2p.io").unwrap();
for peer in &BOOTNODES {
kad.add_address(&PeerId::from_str(peer).unwrap(), bootaddr.clone());
}
}
Ok(kad)
}
fn gossipsub(local_key: Keypair) -> Result<Gossipsub, TatamiError> {
let message_id_fn = |message: &GossipsubMessage| {
let mut s = DefaultHasher::new();
message.data.hash(&mut s);
MessageId::from(s.finish().to_string())
};
let gossipsub_config = GossipsubConfigBuilder::default()
.heartbeat_interval(Duration::from_secs(10)) .validation_mode(ValidationMode::Strict) .message_id_fn(message_id_fn) .build()
.expect("Valid config");
let mut gossipsub = Gossipsub::new(MessageAuthenticity::Signed(local_key), gossipsub_config)
.or(Err(TatamiError::Generic))?;
gossipsub.subscribe(&Satellite::topic()).unwrap();
Ok(gossipsub)
}
}