wip: update nostr sdk
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m35s

This commit is contained in:
2026-02-05 09:29:47 +07:00
parent 0e756fb6c3
commit fce4c1bbcd
18 changed files with 391 additions and 252 deletions

63
Cargo.lock generated
View File

@@ -1264,7 +1264,7 @@ dependencies = [
[[package]]
name = "collections"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"indexmap",
"rustc-hash 2.1.1",
@@ -1709,7 +1709,7 @@ dependencies = [
[[package]]
name = "derive_refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"proc-macro2",
"quote",
@@ -2632,7 +2632,7 @@ dependencies = [
[[package]]
name = "gpui"
version = "0.2.2"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"anyhow",
"as-raw-xcb-connection",
@@ -2734,7 +2734,7 @@ dependencies = [
[[package]]
name = "gpui_macros"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -2745,7 +2745,7 @@ dependencies = [
[[package]]
name = "gpui_tokio"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"anyhow",
"gpui",
@@ -2967,7 +2967,7 @@ dependencies = [
[[package]]
name = "http_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"anyhow",
"async-compression",
@@ -2992,7 +2992,7 @@ dependencies = [
[[package]]
name = "http_client_tls"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"rustls",
"rustls-platform-verifier",
@@ -3742,7 +3742,7 @@ dependencies = [
[[package]]
name = "media"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"anyhow",
"bindgen",
@@ -3982,7 +3982,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "nostr"
version = "0.44.1"
source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16"
source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd"
dependencies = [
"aes",
"base64",
@@ -4007,7 +4007,7 @@ dependencies = [
[[package]]
name = "nostr-connect"
version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16"
source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd"
dependencies = [
"async-utility",
"futures-core",
@@ -4020,7 +4020,7 @@ dependencies = [
[[package]]
name = "nostr-database"
version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16"
source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd"
dependencies = [
"btreecap",
"flatbuffers",
@@ -4032,15 +4032,27 @@ dependencies = [
[[package]]
name = "nostr-gossip"
version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16"
source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd"
dependencies = [
"nostr",
]
[[package]]
name = "nostr-gossip-memory"
version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd"
dependencies = [
"indexmap",
"lru",
"nostr",
"nostr-gossip",
"tokio",
]
[[package]]
name = "nostr-lmdb"
version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16"
source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd"
dependencies = [
"async-utility",
"flume",
@@ -4054,7 +4066,7 @@ dependencies = [
[[package]]
name = "nostr-sdk"
version = "0.44.1"
source = "git+https://github.com/rust-nostr/nostr#c25727493b52af561ed6b6aca43cf866ef7aeb16"
source = "git+https://github.com/rust-nostr/nostr#8b6a6d797f0a7b7fd9147860b3cb00bdd6fc92cd"
dependencies = [
"async-utility",
"async-wsocket",
@@ -4588,7 +4600,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "perf"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"collections",
"serde",
@@ -5237,7 +5249,7 @@ dependencies = [
[[package]]
name = "refineable"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"derive_refineable",
]
@@ -5336,7 +5348,7 @@ dependencies = [
[[package]]
name = "reqwest_client"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"anyhow",
"bytes",
@@ -5391,7 +5403,7 @@ dependencies = [
[[package]]
name = "rope"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"arrayvec",
"log",
@@ -5670,7 +5682,7 @@ dependencies = [
[[package]]
name = "scheduler"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"async-task",
"backtrace",
@@ -6189,6 +6201,7 @@ dependencies = [
"gpui_tokio",
"log",
"nostr-connect",
"nostr-gossip-memory",
"nostr-lmdb",
"nostr-sdk",
"petname",
@@ -6273,7 +6286,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "sum_tree"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"arrayvec",
"log",
@@ -7258,7 +7271,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "util"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"anyhow",
"async-fs",
@@ -7296,7 +7309,7 @@ dependencies = [
[[package]]
name = "util_macros"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"perf",
"quote",
@@ -8772,7 +8785,7 @@ dependencies = [
[[package]]
name = "zlog"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"anyhow",
"chrono",
@@ -8789,7 +8802,7 @@ checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
[[package]]
name = "ztracing"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
dependencies = [
"tracing",
"tracing-subscriber",
@@ -8800,7 +8813,7 @@ dependencies = [
[[package]]
name = "ztracing_macro"
version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#555c002499a4676bac4c2e4aae2fa11f0a2bbddd"
source = "git+https://github.com/zed-industries/zed#49c777779a63a0f44abec3ba7bd490c8b1552667"
[[package]]
name = "zune-core"

View File

@@ -19,6 +19,7 @@ reqwest_client = { git = "https://github.com/zed-industries/zed" }
nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = [ "nip96", "nip59", "nip49", "nip44" ] }
nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" }
# Others
anyhow = "1.0.44"

View File

@@ -243,12 +243,7 @@ impl AutoUpdater {
.author(app_pubkey)
.limit(1);
if let Err(e) = client
.subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts))
.await
{
log::error!("Failed to subscribe to updates: {e}");
};
// TODO
})
}
@@ -285,10 +280,7 @@ impl AutoUpdater {
.author(app_pubkey)
.ids(ids.clone());
// Get all files for this release
client
.subscribe_to(BOOTSTRAP_RELAYS, filter, Some(opts))
.await?;
// TODO
Ok(ids)
} else {

View File

@@ -195,8 +195,8 @@ impl ChatRegistry {
let mut notifications = client.notifications();
let mut processed_events = HashSet::new();
while let Ok(notification) = notifications.recv().await {
let RelayPoolNotification::Message { message, .. } = notification else {
while let Some(notification) = notifications.next().await {
let ClientNotification::Message { message, .. } = notification else {
// Skip non-message notifications
continue;
};
@@ -432,7 +432,7 @@ impl ChatRegistry {
let client = nostr.read(cx).client();
cx.background_spawn(async move {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?;
let contacts = client.database().contacts_public_keys(public_key).await?;
@@ -597,8 +597,8 @@ impl ChatRegistry {
};
// Try with the user's signer
let user_signer = client.signer().await?;
let unwrapped = UnwrappedGift::from_gift_wrap(&user_signer, gift_wrap).await?;
let user_signer = client.signer().context("Signer not found")?;
let unwrapped = UnwrappedGift::from_gift_wrap(user_signer, gift_wrap).await?;
Ok(unwrapped)
}

View File

@@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::time::Duration;
use anyhow::Error;
use anyhow::{Context as AnyhowContext, Error};
use common::EventUtils;
use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task};
use itertools::Itertools;
@@ -325,7 +325,7 @@ impl Room {
let id = SubscriptionId::new(format!("room-{}", self.id));
cx.background_spawn(async move {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?;
// Subscription options
@@ -343,7 +343,9 @@ impl Room {
// Subscribe to get member's gossip relays
client
.subscribe_with_id(id.clone(), filter, Some(opts))
.subscribe(filter)
.close_on(opts)
.with_id(id.clone())
.await?;
}
@@ -458,7 +460,7 @@ impl Room {
let task = self.members_with_relays(cx);
cx.background_spawn(async move {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let current_user_relays = current_user_relays.await;
let mut members = task.await;
@@ -484,10 +486,10 @@ impl Room {
// Construct the gift wrap event
let event =
EventBuilder::gift_wrap(&signer, &receiver, rumor.clone(), vec![]).await?;
EventBuilder::gift_wrap(signer, &receiver, rumor.clone(), vec![]).await?;
// Send the gift wrap event to the messaging relays
match client.send_event_to(relays, &event).await {
match client.send_event(&event).to(&relays).await {
Ok(output) => {
let id = output.id().to_owned();
let auth = output.failed.iter().any(|(_, s)| s.starts_with("auth-"));
@@ -525,7 +527,7 @@ impl Room {
// Construct the gift-wrapped event
let event =
EventBuilder::gift_wrap(&signer, &current_user, rumor.clone(), vec![]).await?;
EventBuilder::gift_wrap(signer, &current_user, rumor.clone(), vec![]).await?;
// Only send a backup message to current user if sent successfully to others
if reports.iter().all(|r| r.is_sent_success()) {
@@ -580,7 +582,7 @@ impl Room {
if let Some(event) = client.database().event_by_id(id).await? {
for url in urls.into_iter() {
let relay = client.pool().relay(url).await?;
let relay = client.relay(url).await?.context("Relay not found")?;
let id = relay.send_event(&event).await?;
let resent: Output<EventId> = Output {

View File

@@ -72,11 +72,10 @@ pub async fn nip96_upload(
let json: Value = res.json().await?;
let config = nip96::ServerConfig::from_json(json.to_string())?;
let signer = if client.has_signer().await {
client.signer().await?
} else {
Keys::generate().into_nostr_signer()
};
let signer = client
.signer()
.cloned()
.unwrap_or(Keys::generate().into_nostr_signer());
let url = upload(&signer, &config, file, None).await?;

View File

@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::time::Duration;
use anyhow::Error;
use anyhow::{Context as AnyhowContext, Error};
use common::{shorten_pubkey, RenderedProfile, RenderedTimestamp, BOOTSTRAP_RELAYS};
use gpui::prelude::FluentBuilder;
use gpui::{
@@ -45,7 +46,7 @@ impl Screening {
let contact_check: Task<Result<(bool, Vec<Profile>), Error>> = cx.background_spawn({
let client = nostr.read(cx).client();
async move {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let signer_pubkey = signer.get_public_key().await?;
// Check if user is in contact list
@@ -74,8 +75,15 @@ impl Screening {
let filter = Filter::new().author(public_key).limit(1);
let mut activity: Option<Timestamp> = None;
// Construct target for subscription
let target = BOOTSTRAP_RELAYS
.into_iter()
.map(|relay| (relay, vec![filter.clone()]))
.collect::<HashMap<_, _>>();
if let Ok(mut stream) = client
.stream_events_from(BOOTSTRAP_RELAYS, filter, Duration::from_secs(2))
.stream_events(target)
.timeout(Duration::from_secs(2))
.await
{
while let Some((_url, event)) = stream.next().await {
@@ -162,12 +170,12 @@ impl Screening {
let public_key = self.profile.public_key();
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let signer = client.signer().await?;
let tag = Tag::public_key_report(public_key, Report::Impersonation);
let event = EventBuilder::report(vec![tag], "").sign(&signer).await?;
let builder = EventBuilder::report(vec![tag], "");
let event = client.sign_event_builder(builder).await?;
// Send the report to the public relays
client.send_event_to(BOOTSTRAP_RELAYS, &event).await?;
client.send_event(&event).to(BOOTSTRAP_RELAYS).await?;
Ok(())
});

View File

@@ -1,7 +1,7 @@
use std::collections::HashSet;
use std::time::Duration;
use anyhow::{anyhow, Error};
use anyhow::{anyhow, Context as AnyhowContext, Error};
use dock::panel::{Panel, PanelEvent};
use gpui::prelude::FluentBuilder;
use gpui::{
@@ -89,7 +89,7 @@ impl MessagingRelayPanel {
}
async fn load(client: &Client) -> Result<Vec<RelayUrl>, Error> {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?;
let filter = Filter::new()
@@ -162,20 +162,17 @@ impl MessagingRelayPanel {
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let urls = write_relays.await;
let signer = client.signer().await?;
let tags: Vec<Tag> = relays
.iter()
.map(|relay| Tag::relay(relay.clone()))
.collect();
let event = EventBuilder::new(Kind::InboxRelays, "")
.tags(tags)
.sign(&signer)
.await?;
let builder = EventBuilder::new(Kind::InboxRelays, "").tags(tags);
let event = client.sign_event_builder(builder).await?;
// Set messaging relays
client.send_event_to(urls, &event).await?;
client.send_event(&event).to(urls).await?;
// Connect to messaging relays
for relay in relays.iter() {

View File

@@ -1,7 +1,7 @@
use std::collections::HashSet;
use std::time::Duration;
use anyhow::{anyhow, Error};
use anyhow::{anyhow, Context as AnyhowContext, Error};
use common::BOOTSTRAP_RELAYS;
use dock::panel::{Panel, PanelEvent};
use gpui::prelude::FluentBuilder;
@@ -96,7 +96,7 @@ impl RelayListPanel {
}
async fn load(client: &Client) -> Result<Vec<(RelayUrl, Option<RelayMetadata>)>, Error> {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?;
let filter = Filter::new()
@@ -167,11 +167,11 @@ impl RelayListPanel {
let relays = self.relays.clone();
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let signer = client.signer().await?;
let event = EventBuilder::relay_list(relays).sign(&signer).await?;
let builder = EventBuilder::relay_list(relays);
let event = client.sign_event_builder(builder).await?;
// Set relay list for current user
client.send_event_to(BOOTSTRAP_RELAYS, &event).await?;
client.send_event(&event).to(BOOTSTRAP_RELAYS).await?;
Ok(())
});

View File

@@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::Duration;
@@ -130,8 +130,8 @@ impl DeviceRegistry {
let mut notifications = client.notifications();
let mut processed_events = HashSet::new();
while let Ok(notification) = notifications.recv().await {
if let RelayPoolNotification::Message {
while let Some(notification) = notifications.next().await {
if let ClientNotification::Message {
message: RelayMessage::Event { event, .. },
..
} = notification
@@ -162,7 +162,7 @@ impl DeviceRegistry {
/// Verify the author of an event
async fn verify_author(client: &Client, event: &Event) -> bool {
if let Ok(signer) = client.signer().await {
if let Some(signer) = client.signer() {
if let Ok(public_key) = signer.get_public_key().await {
return public_key == event.pubkey;
}
@@ -172,7 +172,7 @@ impl DeviceRegistry {
/// Encrypt and store device keys in the local database.
async fn set_keys(client: &Client, secret: &str) -> Result<(), Error> {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?;
// Encrypt the value
@@ -193,7 +193,7 @@ impl DeviceRegistry {
/// Get device keys from the local database.
async fn get_keys(client: &Client) -> Result<Keys, Error> {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?;
let filter = Filter::new()
@@ -278,10 +278,13 @@ impl DeviceRegistry {
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(pkey);
let id = SubscriptionId::new(DEVICE_GIFTWRAP);
if let Err(e) = client
.subscribe_with_id_to(&urls, id, vec![filter], None)
.await
{
// Construct target for subscription
let target = urls
.iter()
.map(|relay| (relay, vec![filter.clone()]))
.collect::<HashMap<_, _>>();
if let Err(e) = client.subscribe(target).with_id(id).await {
log::error!("Failed to subscribe to gift wrap events: {e}");
}
}
@@ -291,10 +294,13 @@ impl DeviceRegistry {
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
let id = SubscriptionId::new(USER_GIFTWRAP);
if let Err(e) = client
.subscribe_with_id_to(urls, id, vec![filter], None)
.await
{
// Construct target for subscription
let target = urls
.iter()
.map(|relay| (relay, vec![filter.clone()]))
.collect::<HashMap<_, _>>();
if let Err(e) = client.subscribe(target).with_id(id).await {
log::error!("Failed to subscribe to gift wrap events: {e}");
}
})
@@ -318,8 +324,15 @@ impl DeviceRegistry {
.author(public_key)
.limit(1);
// Construct target for subscription
let target = urls
.iter()
.map(|relay| (relay, vec![filter.clone()]))
.collect::<HashMap<_, _>>();
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 {
@@ -368,20 +381,17 @@ impl DeviceRegistry {
let n = keys.public_key();
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let signer = client.signer().await?;
let urls = write_relays.await;
// Construct an announcement event
let event = EventBuilder::new(Kind::Custom(10044), "")
.tags(vec![
let builder = EventBuilder::new(Kind::Custom(10044), "").tags(vec![
Tag::custom(TagKind::custom("n"), vec![n]),
Tag::client(app_name()),
])
.sign(&signer)
.await?;
]);
let event = client.sign_event_builder(builder).await?;
// Publish announcement
client.send_event_to(&urls, &event).await?;
client.send_event(&event).to(urls).await?;
// Save device keys to the database
Self::set_keys(&client, &secret).await?;
@@ -461,8 +471,14 @@ impl DeviceRegistry {
.author(public_key)
.since(Timestamp::now());
// Construct target for subscription
let target = urls
.iter()
.map(|relay| (relay, vec![filter.clone()]))
.collect::<HashMap<_, _>>();
// Subscribe to the device key requests on user's write relays
client.subscribe_to(&urls, vec![filter], None).await?;
client.subscribe(target).await?;
Ok(())
});
@@ -487,8 +503,14 @@ impl DeviceRegistry {
.author(public_key)
.since(Timestamp::now());
// Construct target for subscription
let target = urls
.iter()
.map(|relay| (relay, vec![filter.clone()]))
.collect::<HashMap<_, _>>();
// Subscribe to the device key requests on user's write relays
client.subscribe_to(&urls, vec![filter], None).await?;
client.subscribe(target).await?;
Ok(())
});
@@ -508,7 +530,7 @@ impl DeviceRegistry {
let app_pubkey = app_keys.public_key();
let task: Task<Result<Option<Keys>, Error>> = cx.background_spawn(async move {
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?;
let filter = Filter::new()
@@ -538,16 +560,14 @@ impl DeviceRegistry {
let urls = write_relays.await;
// Construct an event for device key request
let event = EventBuilder::new(Kind::Custom(4454), "")
.tags(vec![
let builder = EventBuilder::new(Kind::Custom(4454), "").tags(vec![
Tag::client(app_name()),
Tag::custom(TagKind::custom("P"), vec![app_pubkey]),
])
.sign(&signer)
.await?;
]);
let event = client.sign_event_builder(builder).await?;
// Send the event to write relays
client.send_event_to(&urls, &event).await?;
client.send_event(&event).to(urls).await?;
Ok(None)
}
@@ -625,7 +645,7 @@ impl DeviceRegistry {
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let urls = write_relays.await;
let signer = client.signer().await?;
let signer = client.signer().context("Signer not found")?;
// Get device keys
let keys = Self::get_keys(&client).await?;
@@ -646,16 +666,14 @@ impl DeviceRegistry {
//
// P tag: the current device's public key
// p tag: the requester's public key
let event = EventBuilder::new(Kind::Custom(4455), payload)
.tags(vec![
let builder = EventBuilder::new(Kind::Custom(4455), payload).tags(vec![
Tag::custom(TagKind::custom("P"), vec![keys.public_key()]),
Tag::public_key(target),
])
.sign(&signer)
.await?;
]);
let event = client.sign_event_builder(builder).await?;
// Send the response event to the user's relay list
client.send_event_to(&urls, &event).await?;
client.send_event(&event).to(urls).await?;
Ok(())
});

View File

@@ -139,20 +139,14 @@ impl PersonRegistry {
/// Handle nostr notifications
async fn handle_notifications(client: &Client, tx: &flume::Sender<Dispatch>) {
let mut notifications = client.notifications();
let mut processed_events = HashSet::new();
while let Ok(notification) = notifications.recv().await {
let RelayPoolNotification::Message { message, .. } = notification else {
while let Some(notification) = notifications.next().await {
let ClientNotification::Message { message, .. } = notification else {
// Skip if the notification is not a message
continue;
};
if let RelayMessage::Event { event, .. } = message {
if !processed_events.insert(event.id) {
// Skip if the event has already been processed
continue;
}
match event.kind {
Kind::Metadata => {
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
@@ -230,9 +224,13 @@ impl PersonRegistry {
.authors(authors)
.limit(limit);
client
.subscribe_to(BOOTSTRAP_RELAYS, 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(())
}

View File

@@ -4,7 +4,7 @@ use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use anyhow::{anyhow, Error};
use anyhow::{anyhow, Context as AnyhowContext, Error};
use gpui::{
App, AppContext, Context, Entity, Global, IntoElement, ParentElement, SharedString, Styled,
Subscription, Task, Window,
@@ -137,8 +137,8 @@ impl RelayAuth {
async fn handle_notifications(client: &Client, tx: &flume::Sender<AuthRequest>) {
let mut notifications = client.notifications();
while let Ok(notification) = notifications.recv().await {
if let RelayPoolNotification::Message {
while let Some(notification) = notifications.next().await {
if let ClientNotification::Message {
message: RelayMessage::Auth { challenge },
relay_url,
} = notification
@@ -184,34 +184,33 @@ impl RelayAuth {
let url_clone = url.clone();
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let signer = client.signer().await?;
// Construct event
let event: Event = EventBuilder::auth(challenge_clone, url_clone.clone())
.sign(&signer)
.await?;
let builder = EventBuilder::auth(challenge_clone, url_clone.clone());
let event = client.sign_event_builder(builder).await?;
// Get the event ID
let id = event.id;
// Get the relay
let relay = client.pool().relay(url_clone).await?;
let relay = client.relay(url_clone).await?.context("Relay not found")?;
let relay_url = relay.url();
// Subscribe to notifications
let mut notifications = relay.notifications();
// Send the AUTH message
relay.send_msg(ClientMessage::Auth(Cow::Borrowed(&event)))?;
relay
.send_msg(ClientMessage::Auth(Cow::Borrowed(&event)))
.await?;
while let Ok(notification) = notifications.recv().await {
while let Some(notification) = notifications.next().await {
match notification {
RelayNotification::Message {
message: RelayMessage::Ok { event_id, .. },
} => {
if id == event_id {
// Re-subscribe to previous subscription
relay.resubscribe().await?;
// relay.resubscribe().await?;
// Get all pending events that need to be resent
let mut tracker = tracker().write().await;
@@ -228,7 +227,6 @@ impl RelayAuth {
}
}
RelayNotification::AuthenticationFailed => break,
RelayNotification::Shutdown => break,
_ => {}
}
}

View File

@@ -136,7 +136,7 @@ impl AppSettings {
}
fn new(cx: &mut Context<Self>) -> Self {
let load_settings = Self::get_from_database(false, cx);
let load_settings = Self::get_from_database(cx);
let mut tasks = smallvec![];
let mut subscriptions = smallvec![];
@@ -169,12 +169,10 @@ impl AppSettings {
}
/// Get settings from the database
///
/// If `current_user` is true, the settings will be retrieved for current user.
/// Otherwise, Coop will load the latest settings from the database.
fn get_from_database(current_user: bool, cx: &App) -> Task<Result<Settings, Error>> {
fn get_from_database(cx: &App) -> Task<Result<Settings, Error>> {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let current_user = nostr.read(cx).identity().read(cx).public_key;
cx.background_spawn(async move {
// Construct a filter to get the latest settings
@@ -183,10 +181,7 @@ impl AppSettings {
.identifier(SETTINGS_IDENTIFIER)
.limit(1);
if current_user {
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
if let Some(public_key) = current_user {
// Push author to the filter
filter = filter.author(public_key);
}
@@ -201,7 +196,7 @@ impl AppSettings {
/// Load settings
pub fn load(&mut self, cx: &mut Context<Self>) {
let task = Self::get_from_database(true, cx);
let task = Self::get_from_database(cx);
self._tasks.push(
// Run task in the background
@@ -221,18 +216,17 @@ impl AppSettings {
pub fn save(&mut self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let public_key = nostr.read(cx).identity().read(cx).public_key();
if let Ok(content) = serde_json::to_string(&self.values) {
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
let event = EventBuilder::new(Kind::ApplicationSpecificData, content)
.tag(Tag::identifier(SETTINGS_IDENTIFIER))
.build(public_key)
.sign(&Keys::generate())
.await?;
// Save event to the local database without sending to relays
client.database().save_event(&event).await?;
Ok(())

View File

@@ -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

View File

@@ -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

View File

@@ -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,8 +759,7 @@ impl NostrRegistry {
// Update the signer
self.set_signer(keys, false, cx);
// Spawn a task to set metadata and write the credentials
cx.background_spawn(async move {
// 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();
@@ -754,12 +769,15 @@ impl NostrRegistry {
.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);
// Update user's metadata
let task = self.set_metadata(&metadata, cx);
// Spawn a task to write the credentials
cx.background_spawn(async move {
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![
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"]),
])
.sign(&signer)
.await?;
]);
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 {

View 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?) })
}
}

View File

@@ -246,8 +246,7 @@ impl Element for ContextMenu {
let menu = PopupMenu::build(window, cx, |menu, window, cx| {
(builder)(menu, window, cx)
})
.into_element();
});
let _subscription = window.subscribe(&menu, cx, {
let shared_state = shared_state.clone();