wip: update nostr sdk
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m35s
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m35s
This commit is contained in:
@@ -10,6 +10,7 @@ common = { path = "../common" }
|
||||
nostr-sdk.workspace = true
|
||||
nostr-lmdb.workspace = true
|
||||
nostr-connect.workspace = true
|
||||
nostr-gossip-memory.workspace = true
|
||||
|
||||
gpui.workspace = true
|
||||
gpui_tokio.workspace = true
|
||||
|
||||
@@ -15,9 +15,9 @@ impl RelayState {
|
||||
}
|
||||
|
||||
/// Identity
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Identity {
|
||||
/// The public key of the account
|
||||
/// Signer's public key
|
||||
pub public_key: Option<PublicKey>,
|
||||
|
||||
/// Whether the identity is owned by the user
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
@@ -14,12 +15,14 @@ mod event;
|
||||
mod gossip;
|
||||
mod identity;
|
||||
mod nip05;
|
||||
mod signer;
|
||||
|
||||
pub use device::*;
|
||||
pub use event::*;
|
||||
pub use gossip::*;
|
||||
pub use identity::*;
|
||||
pub use nip05::*;
|
||||
pub use signer::*;
|
||||
|
||||
use crate::identity::Identity;
|
||||
|
||||
@@ -68,6 +71,9 @@ pub struct NostrRegistry {
|
||||
/// Nostr client
|
||||
client: Client,
|
||||
|
||||
/// Nostr signer
|
||||
signer: Arc<CoopSigner>,
|
||||
|
||||
/// App keys
|
||||
///
|
||||
/// Used for Nostr Connect and NIP-4e operations
|
||||
@@ -108,14 +114,6 @@ impl NostrRegistry {
|
||||
.install_default()
|
||||
.ok();
|
||||
|
||||
// Construct the nostr client options
|
||||
let opts = ClientOptions::new()
|
||||
.automatic_authentication(false)
|
||||
.verify_subscriptions(false)
|
||||
.sleep_when_idle(SleepWhenIdle::Enabled {
|
||||
timeout: Duration::from_secs(600),
|
||||
});
|
||||
|
||||
// Construct the lmdb
|
||||
let lmdb = cx.foreground_executor().block_on(async move {
|
||||
NostrLmdb::open(config_dir().join("nostr"))
|
||||
@@ -123,9 +121,23 @@ impl NostrRegistry {
|
||||
.expect("Failed to initialize database")
|
||||
});
|
||||
|
||||
// Construct the nostr signer
|
||||
let keys = Keys::generate();
|
||||
let signer = Arc::new(CoopSigner::new(keys));
|
||||
|
||||
// Construct the nostr client
|
||||
let client = ClientBuilder::default().database(lmdb).opts(opts).build();
|
||||
let _ = tracker();
|
||||
let client = ClientBuilder::default()
|
||||
.signer(signer.clone())
|
||||
.database(lmdb)
|
||||
.automatic_authentication(false)
|
||||
.verify_subscriptions(false)
|
||||
.sleep_when_idle(SleepWhenIdle::Enabled {
|
||||
timeout: Duration::from_secs(600),
|
||||
})
|
||||
.build();
|
||||
|
||||
// Construct the event tracker
|
||||
let _tracker = tracker();
|
||||
|
||||
// Get the app keys
|
||||
let app_keys = Self::create_or_init_app_keys().unwrap();
|
||||
@@ -135,7 +147,7 @@ impl NostrRegistry {
|
||||
let async_gossip = gossip.downgrade();
|
||||
|
||||
// Construct the identity entity
|
||||
let identity = cx.new(|_| Identity::default());
|
||||
let identity = cx.new(|_| Identity::new());
|
||||
|
||||
// Channel for communication between nostr and gpui
|
||||
let (tx, rx) = flume::bounded::<Event>(2048);
|
||||
@@ -207,6 +219,7 @@ impl NostrRegistry {
|
||||
|
||||
Self {
|
||||
client,
|
||||
signer,
|
||||
app_keys,
|
||||
identity,
|
||||
gossip,
|
||||
@@ -239,8 +252,8 @@ impl NostrRegistry {
|
||||
let mut notifications = client.notifications();
|
||||
let mut processed_events = HashSet::new();
|
||||
|
||||
while let Ok(notification) = notifications.recv().await {
|
||||
if let RelayPoolNotification::Message { message, relay_url } = notification {
|
||||
while let Some(notification) = notifications.next().await {
|
||||
if let ClientNotification::Message { message, relay_url } = notification {
|
||||
match message {
|
||||
RelayMessage::Event {
|
||||
event,
|
||||
@@ -325,9 +338,13 @@ impl NostrRegistry {
|
||||
.author(event.pubkey)
|
||||
.limit(1);
|
||||
|
||||
client
|
||||
.subscribe_to(write_relays, vec![inbox, announcement], Some(opts))
|
||||
.await?;
|
||||
// Construct target for subscription
|
||||
let target = write_relays
|
||||
.into_iter()
|
||||
.map(|relay| (relay, vec![inbox.clone(), announcement.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
client.subscribe(target).close_on(opts).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -433,21 +450,21 @@ impl NostrRegistry {
|
||||
}
|
||||
|
||||
/// Set the signer for the nostr client and verify the public key
|
||||
pub fn set_signer<T>(&mut self, signer: T, owned: bool, cx: &mut Context<Self>)
|
||||
pub fn set_signer<T>(&mut self, new: T, owned: bool, cx: &mut Context<Self>)
|
||||
where
|
||||
T: NostrSigner + 'static,
|
||||
{
|
||||
let client = self.client();
|
||||
let identity = self.identity.downgrade();
|
||||
let signer = self.signer.clone();
|
||||
|
||||
// Create a task to update the signer and verify the public key
|
||||
let task: Task<Result<PublicKey, Error>> = cx.background_spawn(async move {
|
||||
// Update signer
|
||||
client.set_signer(signer).await;
|
||||
signer.switch(new).await;
|
||||
|
||||
// Verify signer
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
log::info!("test: {public_key:?}");
|
||||
|
||||
Ok(public_key)
|
||||
});
|
||||
@@ -471,31 +488,6 @@ impl NostrRegistry {
|
||||
}));
|
||||
}
|
||||
|
||||
/// Unset the current signer
|
||||
pub fn unset_signer(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
let async_identity = self.identity.downgrade();
|
||||
|
||||
self.tasks.push(cx.spawn(async move |_this, cx| {
|
||||
// Unset the signer from nostr client
|
||||
cx.background_executor()
|
||||
.await_on_background(async move {
|
||||
client.unset_signer().await;
|
||||
})
|
||||
.await;
|
||||
|
||||
// Unset the current identity
|
||||
async_identity
|
||||
.update(cx, |this, cx| {
|
||||
this.unset_public_key();
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
// Get relay list for current user
|
||||
fn get_relay_list(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
@@ -508,8 +500,15 @@ impl NostrRegistry {
|
||||
.author(public_key)
|
||||
.limit(1);
|
||||
|
||||
// Construct targets for subscription
|
||||
let target = BOOTSTRAP_RELAYS
|
||||
.into_iter()
|
||||
.map(|relay| (relay, vec![filter.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let mut stream = client
|
||||
.stream_events_from(BOOTSTRAP_RELAYS, vec![filter], Duration::from_secs(TIMEOUT))
|
||||
.stream_events(target)
|
||||
.timeout(Duration::from_secs(TIMEOUT))
|
||||
.await?;
|
||||
|
||||
while let Some((_url, res)) = stream.next().await {
|
||||
@@ -523,10 +522,14 @@ impl NostrRegistry {
|
||||
.author(public_key)
|
||||
.since(Timestamp::now());
|
||||
|
||||
// Construct targets for subscription
|
||||
let target = BOOTSTRAP_RELAYS
|
||||
.into_iter()
|
||||
.map(|relay| (relay, vec![filter.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
// Subscribe to the relay list events
|
||||
client
|
||||
.subscribe_to(BOOTSTRAP_RELAYS, vec![filter], None)
|
||||
.await?;
|
||||
client.subscribe(target).await?;
|
||||
|
||||
return Ok(RelayState::Set);
|
||||
}
|
||||
@@ -565,8 +568,7 @@ impl NostrRegistry {
|
||||
let write_relays = self.write_relays(&public_key, cx);
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let mut urls = vec![];
|
||||
urls.extend(write_relays.await);
|
||||
let mut urls = write_relays.await;
|
||||
urls.extend(
|
||||
BOOTSTRAP_RELAYS
|
||||
.iter()
|
||||
@@ -590,9 +592,13 @@ impl NostrRegistry {
|
||||
.limit(1)
|
||||
.author(public_key);
|
||||
|
||||
client
|
||||
.subscribe_to(urls, vec![metadata, contact_list], Some(opts))
|
||||
.await?;
|
||||
// Construct targets for subscription
|
||||
let target = urls
|
||||
.into_iter()
|
||||
.map(|relay| (relay, vec![metadata.clone(), contact_list.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
client.subscribe(target).close_on(opts).await?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
@@ -616,9 +622,16 @@ impl NostrRegistry {
|
||||
.author(public_key)
|
||||
.limit(1);
|
||||
|
||||
// Construct targets for subscription
|
||||
let target = urls
|
||||
.iter()
|
||||
.map(|relay| (relay, vec![filter.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
// Stream events from the write relays
|
||||
let mut stream = client
|
||||
.stream_events_from(&urls, vec![filter], Duration::from_secs(TIMEOUT))
|
||||
.stream_events(target)
|
||||
.timeout(Duration::from_secs(TIMEOUT))
|
||||
.await?;
|
||||
|
||||
while let Some((_url, res)) = stream.next().await {
|
||||
@@ -632,8 +645,14 @@ impl NostrRegistry {
|
||||
.author(public_key)
|
||||
.since(Timestamp::now());
|
||||
|
||||
// Construct targets for subscription
|
||||
let target = urls
|
||||
.iter()
|
||||
.map(|relay| (relay, vec![filter.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
// Subscribe to the relay list events
|
||||
client.subscribe_to(&urls, vec![filter], None).await?;
|
||||
client.subscribe(target).await?;
|
||||
|
||||
return Ok(RelayState::Set);
|
||||
}
|
||||
@@ -687,13 +706,13 @@ impl NostrRegistry {
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let urls = write_relays.await;
|
||||
let signer = client.signer().await?;
|
||||
|
||||
// Sign the new metadata event
|
||||
let event = EventBuilder::metadata(&metadata).sign(&signer).await?;
|
||||
// Build and sign the metadata event
|
||||
let builder = EventBuilder::metadata(&metadata);
|
||||
let event = client.sign_event_builder(builder).await?;
|
||||
|
||||
// Send event to user's write relayss
|
||||
client.send_event_to(urls, &event).await?;
|
||||
client.send_event(&event).to(urls).await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
@@ -728,9 +747,6 @@ impl NostrRegistry {
|
||||
|
||||
/// Create a new identity
|
||||
fn create_identity(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
|
||||
// Generate new keys
|
||||
let keys = Keys::generate();
|
||||
|
||||
// Get write credential task
|
||||
@@ -743,23 +759,25 @@ impl NostrRegistry {
|
||||
// Update the signer
|
||||
self.set_signer(keys, false, cx);
|
||||
|
||||
// Spawn a task to set metadata and write the credentials
|
||||
// Generate a unique name and avatar for the identity
|
||||
let name = petname::petname(2, "-").unwrap_or("Cooper".to_string());
|
||||
let avatar = Url::parse(DEFAULT_AVATAR).unwrap();
|
||||
|
||||
// Construct metadata for the identity
|
||||
let metadata = Metadata::new()
|
||||
.display_name(&name)
|
||||
.name(&name)
|
||||
.picture(avatar);
|
||||
|
||||
// Update user's metadata
|
||||
let task = self.set_metadata(&metadata, cx);
|
||||
|
||||
// Spawn a task to write the credentials
|
||||
cx.background_spawn(async move {
|
||||
let name = petname::petname(2, "-").unwrap_or("Cooper".to_string());
|
||||
let avatar = Url::parse(DEFAULT_AVATAR).unwrap();
|
||||
|
||||
// Construct metadata for the identity
|
||||
let metadata = Metadata::new()
|
||||
.display_name(&name)
|
||||
.name(&name)
|
||||
.picture(avatar);
|
||||
|
||||
// Set metadata for the identity
|
||||
if let Err(e) = client.set_metadata(&metadata).await {
|
||||
log::error!("Failed to set metadata: {}", e);
|
||||
if let Err(e) = task.await {
|
||||
log::error!("Failed to update metadata: {}", e);
|
||||
}
|
||||
|
||||
// Write the credentials
|
||||
if let Err(e) = write_credential.await {
|
||||
log::error!("Failed to write credentials: {}", e);
|
||||
}
|
||||
@@ -874,10 +892,13 @@ impl NostrRegistry {
|
||||
.author(public_key)
|
||||
.limit(1);
|
||||
|
||||
// Subscribe to bootstrap relays
|
||||
client
|
||||
.subscribe_to(BOOTSTRAP_RELAYS, vec![filter], Some(opts))
|
||||
.await?;
|
||||
// Construct target for subscription
|
||||
let target = BOOTSTRAP_RELAYS
|
||||
.into_iter()
|
||||
.map(|relay| (relay, vec![filter.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
client.subscribe(target).close_on(opts).await?;
|
||||
|
||||
Ok(public_key)
|
||||
})
|
||||
@@ -897,9 +918,16 @@ impl NostrRegistry {
|
||||
.kind(Kind::Metadata)
|
||||
.limit(FIND_LIMIT);
|
||||
|
||||
// Construct target for subscription
|
||||
let target = SEARCH_RELAYS
|
||||
.into_iter()
|
||||
.map(|relay| (relay, vec![filter.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
// Stream events from the search relays
|
||||
let mut stream = client
|
||||
.stream_events_from(SEARCH_RELAYS, vec![filter], Duration::from_secs(3))
|
||||
.stream_events(target)
|
||||
.timeout(Duration::from_secs(TIMEOUT))
|
||||
.await?;
|
||||
|
||||
// Collect the results
|
||||
@@ -923,28 +951,31 @@ impl NostrRegistry {
|
||||
let query = query.to_string();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let signer = client.signer().await?;
|
||||
|
||||
// Construct a vertex request event
|
||||
let event = EventBuilder::new(Kind::Custom(5315), "")
|
||||
.tags(vec![
|
||||
Tag::custom(TagKind::custom("param"), vec!["search", &query]),
|
||||
Tag::custom(TagKind::custom("param"), vec!["limit", "10"]),
|
||||
])
|
||||
.sign(&signer)
|
||||
.await?;
|
||||
let builder = EventBuilder::new(Kind::Custom(5315), "").tags(vec![
|
||||
Tag::custom(TagKind::custom("param"), vec!["search", &query]),
|
||||
Tag::custom(TagKind::custom("param"), vec!["limit", "10"]),
|
||||
]);
|
||||
let event = client.sign_event_builder(builder).await?;
|
||||
|
||||
// Send the event to vertex relays
|
||||
let output = client.send_event_to(WOT_RELAYS, &event).await?;
|
||||
let output = client.send_event(&event).to(WOT_RELAYS).await?;
|
||||
|
||||
// Construct a filter to get the response or error from vertex
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::Custom(6315), Kind::Custom(7000)])
|
||||
.event(output.id().to_owned());
|
||||
|
||||
// Stream events from the search relays
|
||||
// Construct target for subscription
|
||||
let target = WOT_RELAYS
|
||||
.into_iter()
|
||||
.map(|relay| (relay, vec![filter.clone()]))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
// Stream events from the wot relays
|
||||
let mut stream = client
|
||||
.stream_events_from(WOT_RELAYS, vec![filter], Duration::from_secs(3))
|
||||
.stream_events(target)
|
||||
.timeout(Duration::from_secs(TIMEOUT))
|
||||
.await?;
|
||||
|
||||
while let Some((_url, res)) = stream.next().await {
|
||||
|
||||
88
crates/state/src/signer.rs
Normal file
88
crates/state/src/signer.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
use nostr_sdk::prelude::*;
|
||||
use smol::lock::RwLock;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CoopSigner {
|
||||
signer: RwLock<Arc<dyn NostrSigner>>,
|
||||
}
|
||||
|
||||
impl CoopSigner {
|
||||
pub fn new<T>(signer: T) -> Self
|
||||
where
|
||||
T: IntoNostrSigner,
|
||||
{
|
||||
Self {
|
||||
signer: RwLock::new(signer.into_nostr_signer()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get(&self) -> Arc<dyn NostrSigner> {
|
||||
self.signer.read().await.clone()
|
||||
}
|
||||
|
||||
pub async fn switch<T>(&self, new: T)
|
||||
where
|
||||
T: IntoNostrSigner,
|
||||
{
|
||||
let mut signer = self.signer.write().await;
|
||||
*signer = new.into_nostr_signer();
|
||||
}
|
||||
}
|
||||
|
||||
impl NostrSigner for CoopSigner {
|
||||
fn backend(&self) -> SignerBackend {
|
||||
SignerBackend::Custom(Cow::Borrowed("custom"))
|
||||
}
|
||||
|
||||
fn get_public_key(&self) -> BoxedFuture<Result<PublicKey, SignerError>> {
|
||||
Box::pin(async move { Ok(self.get().await.get_public_key().await?) })
|
||||
}
|
||||
|
||||
fn sign_event(
|
||||
&self,
|
||||
unsigned: UnsignedEvent,
|
||||
) -> BoxedFuture<std::result::Result<Event, SignerError>> {
|
||||
Box::pin(async move { Ok(self.get().await.sign_event(unsigned).await?) })
|
||||
}
|
||||
|
||||
fn nip04_encrypt<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
content: &'a str,
|
||||
) -> BoxedFuture<'a, std::result::Result<String, SignerError>> {
|
||||
Box::pin(async move { Ok(self.get().await.nip04_encrypt(public_key, content).await?) })
|
||||
}
|
||||
|
||||
fn nip04_decrypt<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
encrypted_content: &'a str,
|
||||
) -> BoxedFuture<'a, std::result::Result<String, SignerError>> {
|
||||
Box::pin(async move {
|
||||
Ok(self
|
||||
.get()
|
||||
.await
|
||||
.nip04_decrypt(public_key, encrypted_content)
|
||||
.await?)
|
||||
})
|
||||
}
|
||||
|
||||
fn nip44_encrypt<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
content: &'a str,
|
||||
) -> BoxedFuture<'a, std::result::Result<String, SignerError>> {
|
||||
Box::pin(async move { Ok(self.get().await.nip44_encrypt(public_key, content).await?) })
|
||||
}
|
||||
|
||||
fn nip44_decrypt<'a>(
|
||||
&'a self,
|
||||
public_key: &'a PublicKey,
|
||||
payload: &'a str,
|
||||
) -> BoxedFuture<'a, std::result::Result<String, SignerError>> {
|
||||
Box::pin(async move { Ok(self.get().await.nip44_decrypt(public_key, payload).await?) })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user