use crate::{
dht::{DHTConfig, DHTKeyMaterial},
name_system::NameSystem,
};
use anyhow::{anyhow, Result};
use libp2p::{self, Multiaddr};
use noosphere_storage::{SphereDb, Storage};
use std::net::Ipv4Addr;
#[cfg(doc)]
use libp2p::kad::KademliaConfig;
pub struct NameSystemBuilder<S, K>
where
S: Storage,
K: DHTKeyMaterial,
{
bootstrap_peers: Option<Vec<Multiaddr>>,
dht_config: DHTConfig,
key_material: Option<K>,
store: Option<SphereDb<S>>,
}
impl<S, K> NameSystemBuilder<S, K>
where
S: Storage,
K: DHTKeyMaterial,
{
pub fn bootstrap_interval(mut self, interval: u64) -> Self {
self.dht_config.bootstrap_interval = interval;
self
}
pub fn bootstrap_peers(mut self, peers: &[Multiaddr]) -> Self {
self.bootstrap_peers = Some(peers.to_owned());
self
}
pub fn key_material(mut self, key_material: &K) -> Self {
self.key_material = Some(key_material.to_owned());
self
}
pub fn listening_port(mut self, port: u16) -> Self {
let mut address = Multiaddr::empty();
address.push(libp2p::multiaddr::Protocol::Ip4(Ipv4Addr::new(
127, 0, 0, 1,
)));
address.push(libp2p::multiaddr::Protocol::Tcp(port));
self.dht_config.listening_address = Some(address);
self
}
pub fn peer_dialing_interval(mut self, interval: u64) -> Self {
self.dht_config.peer_dialing_interval = interval;
self
}
pub fn publication_interval(mut self, interval: u32) -> Self {
self.dht_config.publication_interval = interval;
self
}
pub fn query_timeout(mut self, timeout: u32) -> Self {
self.dht_config.query_timeout = timeout;
self
}
pub fn record_ttl(mut self, interval: u32) -> Self {
self.dht_config.record_ttl = interval;
self
}
pub fn replication_interval(mut self, interval: u32) -> Self {
self.dht_config.replication_interval = interval;
self
}
pub fn store(mut self, store: &SphereDb<S>) -> Self {
self.store = Some(store.to_owned());
self
}
pub fn build(mut self) -> Result<NameSystem<S, K>> {
let key_material = self
.key_material
.take()
.ok_or_else(|| anyhow!("key_material required."))?;
let store = self
.store
.take()
.ok_or_else(|| anyhow!("store required."))?;
Ok(NameSystem::new(
key_material,
store,
self.bootstrap_peers.take(),
self.dht_config,
))
}
}
impl<S, K> Default for NameSystemBuilder<S, K>
where
S: Storage,
K: DHTKeyMaterial,
{
fn default() -> Self {
Self {
bootstrap_peers: None,
dht_config: DHTConfig::default(),
key_material: None,
store: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use noosphere_core::authority::generate_ed25519_key;
use noosphere_storage::{MemoryStorage, SphereDb};
use ucan_key_support::ed25519::Ed25519KeyMaterial;
#[tokio::test]
async fn test_name_system_builder() -> Result<(), anyhow::Error> {
let key_material = generate_ed25519_key();
let store = SphereDb::new(&MemoryStorage::default()).await.unwrap();
let bootstrap_peers: Vec<Multiaddr> = vec![
"/ip4/127.0.0.50/tcp/33333/p2p/12D3KooWH8WgH9mgbMXrKX4veokUznvEn6Ycwg4qaGNi83nLkoUK"
.parse()?,
"/ip4/127.0.0.50/tcp/33334/p2p/12D3KooWMWo6tNGRx1G4TNqvr4SnHyVXSReC3tdX6zoJothXxV2c"
.parse()?,
];
let ns = NameSystemBuilder::default()
.listening_port(30000)
.key_material(&key_material)
.store(&store)
.bootstrap_peers(&bootstrap_peers)
.bootstrap_interval(33)
.peer_dialing_interval(11)
.query_timeout(22)
.publication_interval(60 * 60 * 24 + 1)
.replication_interval(60 * 60 + 1)
.record_ttl(60 * 60 * 24 * 3 + 1)
.build()?;
assert_eq!(ns.key_material.0.as_ref(), key_material.0.as_ref());
assert_eq!(ns.bootstrap_peers.as_ref().unwrap().len(), 2);
assert_eq!(ns.bootstrap_peers.as_ref().unwrap()[0], bootstrap_peers[0],);
assert_eq!(ns.bootstrap_peers.as_ref().unwrap()[1], bootstrap_peers[1]);
assert_eq!(
ns.dht_config.listening_address.as_ref().unwrap(),
&"/ip4/127.0.0.1/tcp/30000".parse()?
);
assert_eq!(ns.dht_config.bootstrap_interval, 33);
assert_eq!(ns.dht_config.peer_dialing_interval, 11);
assert_eq!(ns.dht_config.query_timeout, 22);
assert_eq!(ns.dht_config.publication_interval, 60 * 60 * 24 + 1);
assert_eq!(ns.dht_config.replication_interval, 60 * 60 + 1);
assert_eq!(ns.dht_config.record_ttl, 60 * 60 * 24 * 3 + 1);
if NameSystemBuilder::<MemoryStorage, Ed25519KeyMaterial>::default()
.store(&store)
.build()
.is_ok()
{
panic!("key_material required.");
}
if NameSystemBuilder::<MemoryStorage, Ed25519KeyMaterial>::default()
.key_material(&key_material)
.build()
.is_ok()
{
panic!("store required.");
}
Ok(())
}
}