nostr_types/nip46/
prebunk.rs

1use crate::client::Client;
2use crate::nip46::{BunkerClient, Nip46ConnectionParameters, Nip46Request, Nip46Response};
3use crate::{Error, EventKind, Filter, KeySigner, LockableSigner, PublicKey, RelayUrl, Signer};
4use serde::{Deserialize, Serialize};
5use std::sync::Arc;
6use std::time::Duration;
7use tracing::{event, span, Level};
8
9/// This is a Remote Signer setup that has not yet discovered the user's PublicKey
10/// As a result, it cannot implement Signer yet.
11#[derive(Debug, Serialize, Deserialize)]
12pub struct PreBunkerClient {
13    /// The pubkey of the bunker
14    pub remote_signer_pubkey: PublicKey,
15
16    /// The relay the bunker is listening at
17    pub relay_url: RelayUrl,
18
19    /// The connect secret
20    pub connect_secret: Option<String>,
21
22    /// Our local identity
23    pub local_signer: Arc<KeySigner>,
24
25    /// Timeout
26    pub timeout: Duration,
27}
28
29impl PreBunkerClient {
30    /// Create a new BunkerClient, generating a fresh local identity
31    pub fn new(
32        remote_signer_pubkey: PublicKey,
33        relay_url: RelayUrl,
34        connect_secret: Option<String>,
35        new_password: &str,
36        timeout: Duration,
37    ) -> Result<PreBunkerClient, Error> {
38        let local_signer = Arc::new(KeySigner::generate(new_password, 18)?);
39
40        Ok(PreBunkerClient {
41            remote_signer_pubkey,
42            relay_url,
43            connect_secret,
44            local_signer,
45            timeout,
46        })
47    }
48
49    /// Create a new nip46 client from a URL.
50    ///
51    /// This connects to the relay, but does not contact the bunker yet. Use `connect()` to
52    /// initiate contact with the bunker.
53    pub fn new_from_url(
54        url: &str,
55        new_password: &str,
56        timeout: Duration,
57    ) -> Result<PreBunkerClient, Error> {
58        let Nip46ConnectionParameters {
59            remote_signer_pubkey,
60            relays,
61            secret,
62        } = Nip46ConnectionParameters::from_str(url)?;
63
64        let local_signer = Arc::new(KeySigner::generate(new_password, 18)?);
65
66        Ok(PreBunkerClient {
67            remote_signer_pubkey,
68            relay_url: relays[0].clone(),
69            connect_secret: secret,
70            local_signer,
71            timeout,
72        })
73    }
74
75    /// Is the signer locked?
76    pub fn is_locked(&self) -> bool {
77        self.local_signer.is_locked()
78    }
79
80    /// Unlock (if locked)
81    pub fn unlock(&mut self, password: &str) -> Result<(), Error> {
82        self.local_signer.unlock(password)
83    }
84
85    /// Connect to the relay and bunker, learn our user's PublicKey,
86    /// and return a full BunkerClient which impl Signer
87    pub async fn initialize(&mut self) -> Result<BunkerClient, Error> {
88        let span = span!(Level::DEBUG, "nip46 Prebunk initializing");
89        let _enter = span.enter();
90
91        let client = Client::new(self.relay_url.as_str());
92
93        let connect_response = {
94            let connect_request = {
95                let params = {
96                    let mut params = vec![self.remote_signer_pubkey.as_hex_string()];
97                    if let Some(secret) = &self.connect_secret {
98                        params.push(secret.to_owned());
99                    }
100                    params
101                };
102
103                Nip46Request::new("connect".to_string(), params)
104            };
105
106            event!(Level::DEBUG, "Calling with connect request event");
107            self.call(&client, connect_request).await?
108        };
109
110        if let Some(error) = connect_response.error {
111            if !error.is_empty() {
112                return Err(Error::Nip46Error(error));
113            }
114        }
115
116        // Ask for our pubkey
117        let pubkey_response = {
118            let pubkey_request = {
119                let params = vec![];
120                Nip46Request::new("get_public_key".to_string(), params)
121            };
122
123            event!(Level::DEBUG, "Calling with pubkey request event");
124            self.call(&client, pubkey_request).await?
125        };
126
127        // Verify there is no error
128        if let Some(error) = pubkey_response.error {
129            if !error.is_empty() {
130                return Err(Error::Nip46Error(error));
131            }
132        }
133
134        let public_key = PublicKey::try_from_hex_string(&pubkey_response.result, true)?;
135
136        Ok(BunkerClient {
137            remote_signer_pubkey: self.remote_signer_pubkey,
138            relay_url: self.relay_url.clone(),
139            local_signer: self.local_signer.clone(),
140            public_key,
141            timeout: self.timeout,
142            client,
143        })
144    }
145
146    async fn call(&self, client: &Client, request: Nip46Request) -> Result<Nip46Response, Error> {
147        let span = span!(Level::DEBUG, "nip46 Prebunk callfn");
148        let _enter = span.enter();
149
150        let event = request
151            .to_event(self.remote_signer_pubkey, self.local_signer.clone())
152            .await?;
153
154        // Subscribe
155        let mut filter = Filter::new();
156        filter.add_author(self.remote_signer_pubkey);
157        filter.add_event_kind(EventKind::NostrConnect);
158        filter.add_tag_value('p', self.local_signer.public_key().as_hex_string());
159        filter.limit = Some(1);
160        event!(
161            Level::DEBUG,
162            "calling client subscribe to subscribe to responses from the remote signer"
163        );
164        let sub_id = client.subscribe(filter.clone(), self.timeout).await?;
165
166        // Post event to server
167        let event_id = event.id;
168        event!(Level::DEBUG, "posting our event");
169        client.post_event(event, self.timeout).await?;
170        event!(Level::DEBUG, "waiting for OK response");
171        let (ok, msg) = client.wait_for_ok(event_id, self.timeout).await?;
172        if !ok {
173            return Err(Error::Nip46FailedToPost(msg));
174        }
175
176        // Wait for a response on the subscription
177        event!(
178            Level::DEBUG,
179            "waiting for a response event on the remote signer subscription"
180        );
181        let event = client
182            .wait_for_subscribed_event(sub_id, self.timeout)
183            .await?;
184        let contents = self.local_signer.decrypt_event_contents(&event).await?;
185
186        // Convert into a response
187        let response: Nip46Response = serde_json::from_str(&contents)?;
188
189        Ok(response)
190    }
191}