noosphere_ns/
helpers.rs

1use crate::{DhtClient, DhtConfig, NameResolver, NameSystem};
2use anyhow::Result;
3use async_trait::async_trait;
4use libp2p::Multiaddr;
5use noosphere_core::{
6    authority::generate_ed25519_key,
7    data::{Did, LinkRecord},
8};
9use std::collections::HashMap;
10use tokio::sync::Mutex;
11use ucan::store::UcanJwtStore;
12
13/// An in-process network of [NameSystem] nodes for testing.
14pub struct NameSystemNetwork {
15    nodes: Vec<NameSystem>,
16    address: Multiaddr,
17}
18
19impl NameSystemNetwork {
20    /// [NameSystem] nodes in the network.
21    pub fn nodes(&self) -> &Vec<NameSystem> {
22        &self.nodes
23    }
24
25    /// [NameSystem] nodes in the network.
26    pub fn nodes_mut(&mut self) -> &mut Vec<NameSystem> {
27        &mut self.nodes
28    }
29
30    /// Get reference to `index` [NameSystem] node.
31    pub fn get(&self, index: usize) -> Option<&NameSystem> {
32        self.nodes.get(index)
33    }
34
35    /// Get mutable reference to `index` [NameSystem] node.
36    pub fn get_mut(&mut self, index: usize) -> Option<&mut NameSystem> {
37        self.nodes.get_mut(index)
38    }
39
40    /// An address of a node in the network to join.
41    pub fn address(&self) -> &Multiaddr {
42        &self.address
43    }
44
45    /// Generates a DHT network bootstrap node with `node_count`
46    /// [NameSystem]s connected, each with a corresponding owner sphere.
47    /// Useful for tests. All nodes share an underlying (cloned) store
48    /// that may share state.
49    pub async fn generate<S: UcanJwtStore + Clone + 'static>(
50        node_count: usize,
51        store: Option<S>,
52    ) -> Result<Self> {
53        let mut bootstrap_address: Option<Multiaddr> = None;
54        let mut nodes = vec![];
55        for _ in 0..node_count {
56            let key = generate_ed25519_key();
57            let node = NameSystem::new(&key, DhtConfig::default(), store.clone())?;
58            let address = node.listen("/ip4/127.0.0.1/tcp/0".parse()?).await?;
59            if let Some(address) = bootstrap_address.as_ref() {
60                node.add_peers(vec![address.to_owned()]).await?;
61            } else {
62                bootstrap_address = Some(address);
63            }
64            nodes.push(node);
65        }
66        Ok(NameSystemNetwork {
67            nodes,
68            address: bootstrap_address.unwrap(),
69        })
70    }
71}
72
73pub struct KeyValueNameResolver {
74    store: Mutex<HashMap<Did, LinkRecord>>,
75}
76
77impl KeyValueNameResolver {
78    pub fn new() -> Self {
79        KeyValueNameResolver {
80            store: Mutex::new(HashMap::new()),
81        }
82    }
83}
84
85impl Default for KeyValueNameResolver {
86    fn default() -> Self {
87        Self::new()
88    }
89}
90
91#[async_trait]
92impl NameResolver for KeyValueNameResolver {
93    async fn publish(&self, record: LinkRecord) -> Result<()> {
94        let mut store = self.store.lock().await;
95        let did_id = Did(record.to_sphere_identity().into());
96        store.insert(did_id, record);
97        Ok(())
98    }
99
100    async fn resolve(&self, identity: &Did) -> Result<Option<LinkRecord>> {
101        let store = self.store.lock().await;
102        Ok(store.get(identity).map(|record| record.to_owned()))
103    }
104}
105
106#[cfg(test)]
107mod test {
108    use super::*;
109    use crate::name_resolver_tests;
110    async fn before_name_resolver_tests() -> Result<KeyValueNameResolver> {
111        Ok(KeyValueNameResolver::new())
112    }
113    name_resolver_tests!(KeyValueNameResolver, before_name_resolver_tests);
114}