noosphere_ns/
name_system.rs1use crate::{
2 dht::{DhtConfig, DhtError, DhtNode, DhtRecord, NetworkInfo, Peer},
3 utils::make_p2p_address,
4 validator::RecordValidator,
5 DhtClient, PeerId,
6};
7use anyhow::{anyhow, Result};
8use async_trait::async_trait;
9use libp2p::{identity::Keypair, Multiaddr};
10use noosphere_core::data::{Did, LinkRecord};
11use ucan::{crypto::KeyMaterial, store::UcanJwtStore};
12use ucan_key_support::ed25519::Ed25519KeyMaterial;
13
14#[cfg(doc)]
15use cid::Cid;
16
17pub static BOOTSTRAP_PEERS_ADDRESSES: [&str; 1] =
18 ["/ip4/134.122.20.28/tcp/6666/p2p/12D3KooWPyjAB3XWUboGmLLPkR53fTyj4GaNi65RvQ61BVwqV4HG"];
19
20lazy_static! {
21 pub static ref BOOTSTRAP_PEERS: [Multiaddr; 1] = BOOTSTRAP_PEERS_ADDRESSES.map(|addr| addr.parse().expect("parseable"));
24}
25
26pub trait NameSystemKeyMaterial: KeyMaterial + Clone {
27 fn to_dht_keypair(&self) -> anyhow::Result<Keypair>;
28}
29
30impl NameSystemKeyMaterial for Ed25519KeyMaterial {
31 fn to_dht_keypair(&self) -> anyhow::Result<Keypair> {
32 pub const ED25519_KEY_LENGTH: usize = 32;
33 let mut bytes: [u8; ED25519_KEY_LENGTH] = [0u8; ED25519_KEY_LENGTH];
34 bytes[..ED25519_KEY_LENGTH].copy_from_slice(
35 self.1
36 .ok_or_else(|| anyhow!("Private key required in order to deserialize."))?
37 .as_ref(),
38 );
39 let kp = Keypair::ed25519_from_bytes(&mut bytes)
40 .map_err(|_| anyhow::anyhow!("Could not decode ED25519 key."))?;
41 Ok(kp)
42 }
43}
44
45pub struct NameSystem {
60 pub(crate) dht: DhtNode,
61}
62
63impl NameSystem {
64 pub fn new<K: NameSystemKeyMaterial, S: UcanJwtStore + 'static>(
65 key_material: &K,
66 dht_config: DhtConfig,
67 store: Option<S>,
68 ) -> Result<Self> {
69 let keypair = key_material.to_dht_keypair()?;
70 let validator = store.map(|s| RecordValidator::new(s));
71
72 Ok(NameSystem {
73 dht: DhtNode::new(keypair, dht_config, validator)?,
74 })
75 }
76}
77
78#[async_trait]
79impl DhtClient for NameSystem {
80 async fn network_info(&self) -> Result<NetworkInfo> {
82 self.dht.network_info().await.map_err(|e| e.into())
83 }
84
85 fn peer_id(&self) -> &PeerId {
87 self.dht.peer_id()
88 }
89
90 async fn add_peers(&self, peers: Vec<Multiaddr>) -> Result<()> {
93 self.dht.add_peers(peers).await.map_err(|e| e.into())
94 }
95
96 async fn peers(&self) -> Result<Vec<Peer>> {
98 self.dht.peers().await.map_err(|e| e.into())
99 }
100
101 async fn listen(&self, listening_address: Multiaddr) -> Result<Multiaddr> {
103 self.dht
104 .listen(listening_address)
105 .await
106 .map_err(|e| e.into())
107 }
108
109 async fn stop_listening(&self) -> Result<()> {
111 self.dht.stop_listening().await.map_err(|e| e.into())
112 }
113
114 async fn bootstrap(&self) -> Result<()> {
116 self.dht.bootstrap().await.map_err(|e| e.into())
117 }
118
119 async fn address(&self) -> Result<Option<Multiaddr>> {
121 let mut addresses = self
122 .dht
123 .addresses()
124 .await
125 .map_err(<DhtError as Into<anyhow::Error>>::into)?;
126 if !addresses.is_empty() {
127 let peer_id = self.peer_id().to_owned();
128 let address = make_p2p_address(addresses.swap_remove(0), peer_id);
129 Ok(Some(address))
130 } else {
131 Ok(None)
132 }
133 }
134
135 async fn put_record(&self, record: LinkRecord, quorum: usize) -> Result<()> {
136 let identity = record.to_sphere_identity();
137 let record_bytes: Vec<u8> = record.try_into()?;
138 match self
139 .dht
140 .put_record(identity.as_bytes(), &record_bytes, quorum)
141 .await
142 {
143 Ok(_) => Ok(()),
144 Err(e) => Err(anyhow!(e.to_string())),
145 }
146 }
147
148 async fn get_record(&self, identity: &Did) -> Result<Option<LinkRecord>> {
149 match self.dht.get_record(identity.as_bytes()).await {
150 Ok(DhtRecord { key: _, value }) => match value {
151 Some(value) => Ok(Some(LinkRecord::try_from(value)?)),
152 None => Ok(None),
153 },
154 Err(e) => Err(anyhow!(e.to_string())),
155 }
156 }
157}
158
159#[cfg(test)]
160mod test {
161 use super::*;
162 use noosphere_core::authority::generate_ed25519_key;
163
164 #[test]
165 fn bootstrap_peers_parseable() {
166 assert_eq!(BOOTSTRAP_PEERS.len(), 1);
169 }
170
171 use crate::name_resolver_tests;
172 async fn before_name_resolver_tests() -> Result<NameSystem> {
173 let ns = {
174 let key_material = generate_ed25519_key();
175 let store = SphereDb::new(&MemoryStorage::default()).await.unwrap();
176 let ns = NameSystemBuilder::default()
177 .ucan_store(store)
178 .key_material(&key_material)
179 .listening_port(0)
180 .use_test_config()
181 .build()
182 .await
183 .unwrap();
184 ns.bootstrap().await.unwrap();
185 ns
186 };
187 Ok(ns)
188 }
189 name_resolver_tests!(NameSystem, before_name_resolver_tests);
190
191 use crate::dht_client_tests;
192 use crate::{utils::wait_for_peers, NameSystemBuilder};
193 use noosphere_storage::{MemoryStorage, SphereDb};
194 use std::sync::Arc;
195 use tokio::sync::Mutex;
196
197 struct DataPlaceholder {
201 _bootstrap: NameSystem,
202 _ns: Arc<Mutex<NameSystem>>,
203 }
204
205 async fn before_each() -> Result<(DataPlaceholder, Arc<Mutex<NameSystem>>)> {
206 let (bootstrap, bootstrap_address) = {
207 let key_material = generate_ed25519_key();
208 let store = SphereDb::new(&MemoryStorage::default()).await.unwrap();
209 let ns = NameSystemBuilder::default()
210 .ucan_store(store)
211 .key_material(&key_material)
212 .listening_port(0)
213 .use_test_config()
214 .build()
215 .await
216 .unwrap();
217 ns.bootstrap().await.unwrap();
218 let address = ns.address().await?.unwrap();
219 (ns, address)
220 };
221
222 let ns = {
223 let key_material = generate_ed25519_key();
224 let store = SphereDb::new(&MemoryStorage::default()).await.unwrap();
225 let ns = NameSystemBuilder::default()
226 .ucan_store(store)
227 .key_material(&key_material)
228 .bootstrap_peers(&[bootstrap_address.clone()])
229 .use_test_config()
230 .build()
231 .await
232 .unwrap();
233 ns.bootstrap().await.unwrap();
234 wait_for_peers::<NameSystem>(&ns, 1).await?;
235 ns
236 };
237
238 let client = Arc::new(Mutex::new(ns));
239 let reference = client.clone();
243 let data = DataPlaceholder {
244 _ns: reference,
245 _bootstrap: bootstrap,
246 };
247 Ok((data, client))
248 }
249
250 dht_client_tests!(NameSystem, before_each, DataPlaceholder);
251}