Skip to main content

hashtree_resolver/nostr/
open.rs

1use super::*;
2
3impl NostrRootResolver {
4    /// Observe a mutable root for a bounded window, keeping the subscription open
5    /// after EOSE. Returns the newest usable signed root observed in that window;
6    /// a quiet window is a timeout, not proof that the root does not exist.
7    /// Unlike quick resolution, this does not use cached observations or a quorum
8    /// shortcut. It closes only its own subscription before returning.
9    pub async fn resolve_open(&self, key: &str, window: Duration) -> Result<Cid, ResolverError> {
10        self.observe_roots(key, window, false).await
11    }
12
13    /// Return the first usable authenticated root without waiting for EOSE or
14    /// a relay quorum. A quiet deadline is a timeout, never proof of absence.
15    /// Closes this lookup's subscription before returning.
16    pub async fn resolve_ready(&self, key: &str, timeout: Duration) -> Result<Cid, ResolverError> {
17        self.observe_roots(key, timeout, true).await
18    }
19
20    async fn observe_roots(
21        &self,
22        key: &str,
23        window: Duration,
24        ready: bool,
25    ) -> Result<Cid, ResolverError> {
26        let (pubkey, tree_name) = Self::parse_key(key)?;
27        if self.config.relays.is_empty() {
28            return Err(ResolverError::Network(
29                "No active Nostr relays configured".into(),
30            ));
31        }
32        let filter = Self::build_tree_filter(pubkey, &tree_name);
33        let id = SubscriptionId::generate();
34        // Register before REQ so even an immediate relay response is observed.
35        let mut notifications = self.client.notifications();
36        let mut latest: Option<(VerifiedEvent, Cid)> = None;
37        let observed: Result<Result<(), ResolverError>, _> = tokio::time::timeout(window, async {
38            self.client
39                .subscribe_with_id(id.clone(), filter, None)
40                .await
41                .map_err(|error| ResolverError::Network(error.to_string()))?;
42            loop {
43                let notification = notifications
44                    .recv()
45                    .await
46                    .map_err(|error| ResolverError::Network(error.to_string()))?;
47                // Event notifications are globally deduplicated by the SDK. Raw
48                // messages also cover a root already seen on another subscription.
49                let RelayPoolNotification::Message {
50                    message:
51                        RelayMessage::Event {
52                            subscription_id,
53                            event,
54                        },
55                    ..
56                } = notification
57                else {
58                    continue;
59                };
60                if subscription_id.as_ref() != &id {
61                    continue;
62                }
63                let Ok(event) = VerifiedEvent::try_from(event.into_owned()) else {
64                    continue;
65                };
66                if event.as_event().pubkey != pubkey || !is_matching_tree_event(&event, &tree_name)
67                {
68                    continue;
69                }
70                let Some(cid) = self.cid_from_event(event.as_event()) else {
71                    continue;
72                };
73                if latest.as_ref().is_none_or(|(current, _)| {
74                    is_newer_event(event.as_event(), current.created_at(), Some(current.id()))
75                }) {
76                    latest = Some((event, cid));
77                    if ready {
78                        return Ok(());
79                    }
80                }
81            }
82        })
83        .await;
84        self.client.unsubscribe(&id).await;
85        if let Ok(result) = observed {
86            result?;
87        }
88        latest.map(|(_, cid)| cid).ok_or_else(|| {
89            ResolverError::Network(format!("Timed out waiting for a signed root for {key}"))
90        })
91    }
92}