syncweb_core/node/
discovery.rs1use distributed_topic_tracker::{
2 AutoDiscoveryGossip, Config, DefaultSecretRotation, RecordPublisher, RotationHandle, Topic, TopicId,
3};
4use ed25519_dalek::SigningKey;
5use iroh::{Endpoint, PublicKey};
6use iroh_docs::NamespaceId;
7use iroh_gossip::net::Gossip;
8use std::{collections::HashMap, sync::Arc};
9use tokio::sync::Mutex;
10
11use crate::error::{Result, SyncwebError};
12
13#[derive(Clone)]
14pub struct TopicTracker {
15 gossip: Gossip,
16 signing_key: SigningKey,
17 topics: Arc<Mutex<HashMap<[u8; 32], Topic>>>,
18}
19
20impl TopicTracker {
21 #[must_use]
22 pub fn new(gossip: &Gossip, endpoint: &Endpoint) -> Self {
23 Self {
24 gossip: gossip.clone(),
25 signing_key: SigningKey::from_bytes(&endpoint.secret_key().to_bytes()),
26 topics: Arc::new(Mutex::new(HashMap::new())),
27 }
28 }
29
30 pub async fn announce(&self, namespace_id: NamespaceId) -> Result<()> {
34 self.topic(namespace_id).await?;
35 Ok(())
36 }
37
38 pub async fn find_peers(&self, namespace_id: NamespaceId) -> Result<Vec<PublicKey>> {
42 let topic = self.topic(namespace_id).await?;
43 let receiver = topic
44 .gossip_receiver()
45 .await
46 .map_err(|error| SyncwebError::operation("failed to get discovery receiver", error))?;
47 Ok(receiver
48 .neighbors()
49 .await
50 .map_err(|error| SyncwebError::operation("failed to find discovery peers", error))?
51 .into_iter()
52 .collect())
53 }
54
55 async fn topic(&self, namespace_id: NamespaceId) -> Result<Topic> {
56 let key = *namespace_id.as_bytes();
57 let existing = self.topics.lock().await.get(&key).cloned();
58 if let Some(topic) = existing {
59 return Ok(topic);
60 }
61
62 let topic_id = TopicId::from_hash(&key);
63 let publisher = RecordPublisher::builder(topic_id, self.signing_key.clone(), namespace_id.as_bytes().to_vec())
64 .config(Config::default())
65 .secret_rotation(RotationHandle::new(DefaultSecretRotation))
66 .build();
67 let topic = self
68 .gossip
69 .subscribe_and_join_with_auto_discovery_no_wait(publisher)
70 .await
71 .map_err(|error| SyncwebError::operation("failed to join discovery topic", error))?;
72 self.topics.lock().await.insert(key, topic.clone());
73 Ok(topic)
74 }
75}