1use crate::{dht::DhtConfig, name_system::NameSystem, DhtClient, NameSystemKeyMaterial};
2use anyhow::{anyhow, Result};
3use libp2p::{self, Multiaddr};
4use std::net::Ipv4Addr;
5use ucan::store::UcanJwtStore;
6
7#[cfg(doc)]
8use libp2p::kad::KademliaConfig;
9
10pub struct NameSystemBuilder<K, S>
38where
39 K: NameSystemKeyMaterial + 'static,
40 S: UcanJwtStore + 'static,
41{
42 bootstrap_peers: Option<Vec<Multiaddr>>,
43 listening_address: Option<Multiaddr>,
44 dht_config: DhtConfig,
45 key_material: Option<K>,
46 ucan_store: Option<S>,
47}
48
49impl<K, S> NameSystemBuilder<K, S>
50where
51 K: NameSystemKeyMaterial + 'static,
52 S: UcanJwtStore + 'static,
53{
54 pub fn bootstrap_interval(mut self, interval: u64) -> Self {
58 self.dht_config.bootstrap_interval = interval;
59 self
60 }
61
62 pub fn bootstrap_peers(mut self, peers: &[Multiaddr]) -> Self {
66 self.bootstrap_peers = Some(peers.to_owned());
67 self
68 }
69
70 pub fn key_material(mut self, key_material: &K) -> Self {
72 self.key_material = Some(key_material.to_owned());
73 self
74 }
75
76 pub fn listening_port(mut self, port: u16) -> Self {
79 let mut address = Multiaddr::empty();
80 address.push(libp2p::multiaddr::Protocol::Ip4(Ipv4Addr::new(
81 127, 0, 0, 1,
82 )));
83 address.push(libp2p::multiaddr::Protocol::Tcp(port));
84 self.listening_address = Some(address);
85 self
86 }
87
88 pub fn peer_dialing_interval(mut self, interval: u64) -> Self {
92 self.dht_config.peer_dialing_interval = interval;
93 self
94 }
95
96 pub fn publication_interval(mut self, interval: u32) -> Self {
100 self.dht_config.publication_interval = interval;
101 self
102 }
103
104 pub fn query_timeout(mut self, timeout: u32) -> Self {
106 self.dht_config.query_timeout = timeout;
107 self
108 }
109
110 pub fn ucan_store(mut self, store: S) -> Self {
111 self.ucan_store = Some(store);
112 self
113 }
114
115 pub fn record_ttl(mut self, interval: u32) -> Self {
119 self.dht_config.record_ttl = interval;
120 self
121 }
122
123 pub fn replication_interval(mut self, interval: u32) -> Self {
127 self.dht_config.replication_interval = interval;
128 self
129 }
130
131 pub async fn build(mut self) -> Result<NameSystem> {
133 let key_material = self
134 .key_material
135 .take()
136 .ok_or_else(|| anyhow!("key_material required."))?;
137 let ucan_store = self
138 .ucan_store
139 .ok_or_else(|| anyhow!("ucan_store is required"))?;
140 let ns = NameSystem::new(&key_material, self.dht_config.clone(), Some(ucan_store))?;
141
142 if let Some(listening_address) = self.listening_address {
143 ns.listen(listening_address).await?;
144 }
145
146 if let Some(bootstrap_peers) = self.bootstrap_peers {
147 ns.add_peers(bootstrap_peers).await?;
148 }
149
150 Ok(ns)
151 }
152
153 #[cfg(test)]
154 pub fn use_test_config(mut self) -> Self {
157 self.dht_config.peer_dialing_interval = 1;
158 self
159 }
160}
161
162impl<K, S> Default for NameSystemBuilder<K, S>
163where
164 K: NameSystemKeyMaterial + 'static,
165 S: UcanJwtStore + 'static,
166{
167 fn default() -> Self {
168 Self {
169 bootstrap_peers: None,
170 dht_config: DhtConfig::default(),
171 key_material: None,
172 listening_address: None,
173 ucan_store: None,
174 }
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use libp2p::PeerId;
182 use noosphere_core::authority::generate_ed25519_key;
183 use noosphere_storage::{MemoryStorage, SphereDb};
184 use ucan_key_support::ed25519::Ed25519KeyMaterial;
185
186 #[tokio::test]
187 async fn test_name_system_builder() -> Result<(), anyhow::Error> {
188 let key_material = generate_ed25519_key();
189 let peer_id = {
190 let keypair = key_material.to_dht_keypair()?;
191 PeerId::from(keypair.public())
192 };
193 let store = SphereDb::new(&MemoryStorage::default()).await.unwrap();
194 let bootstrap_peers: Vec<Multiaddr> = vec![
195 "/ip4/127.0.0.50/tcp/33333/p2p/12D3KooWH8WgH9mgbMXrKX4veokUznvEn6Ycwg4qaGNi83nLkoUK"
196 .parse()?,
197 "/ip4/127.0.0.50/tcp/33334/p2p/12D3KooWMWo6tNGRx1G4TNqvr4SnHyVXSReC3tdX6zoJothXxV2c"
198 .parse()?,
199 ];
200
201 let ns = NameSystemBuilder::default()
202 .listening_port(30000)
203 .ucan_store(store.clone())
204 .key_material(&key_material)
205 .bootstrap_peers(&bootstrap_peers)
206 .bootstrap_interval(33)
207 .peer_dialing_interval(11)
208 .query_timeout(22)
209 .publication_interval(60 * 60 * 24 + 1)
210 .replication_interval(60 * 60 + 1)
211 .record_ttl(60 * 60 * 24 * 3 + 1)
212 .build()
213 .await?;
214
215 assert_eq!(ns.dht.peer_id(), &peer_id);
216 assert_eq!(
217 ns.address().await?.unwrap(),
218 format!("/ip4/127.0.0.1/tcp/30000/p2p/{}", peer_id).parse()?
219 );
220 let dht_config = ns.dht.config();
221 assert_eq!(dht_config.bootstrap_interval, 33);
222 assert_eq!(dht_config.peer_dialing_interval, 11);
223 assert_eq!(dht_config.query_timeout, 22);
224 assert_eq!(dht_config.publication_interval, 60 * 60 * 24 + 1);
225 assert_eq!(dht_config.replication_interval, 60 * 60 + 1);
226 assert_eq!(dht_config.record_ttl, 60 * 60 * 24 * 3 + 1);
227
228 if NameSystemBuilder::<Ed25519KeyMaterial, _>::default()
229 .ucan_store(store.clone())
230 .build()
231 .await
232 .is_ok()
233 {
234 panic!("key_material required.");
235 }
236 if NameSystemBuilder::<Ed25519KeyMaterial, SphereDb<MemoryStorage>>::default()
237 .key_material(&key_material)
238 .build()
239 .await
240 .is_ok()
241 {
242 panic!("ucan_store required.");
243 }
244 Ok(())
245 }
246}