update nostr sdk

This commit is contained in:
2026-05-17 14:38:07 +07:00
parent 6d60726f27
commit d2a17e54c4
19 changed files with 630 additions and 612 deletions

View File

@@ -2,6 +2,7 @@ use std::cell::Cell;
use std::collections::HashSet;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;
use anyhow::{Context as AnyhowContext, Error, anyhow};
@@ -11,7 +12,7 @@ use gpui::{
};
use nostr_sdk::prelude::*;
use person::PersonRegistry;
use state::{Announcement, NostrRegistry, StateEvent, TIMEOUT, app_name};
use state::{Announcement, CoopSigner, NostrRegistry, StateEvent, TIMEOUT, app_name};
use theme::ActiveTheme;
use ui::avatar::Avatar;
use ui::button::Button;
@@ -110,6 +111,8 @@ impl DeviceRegistry {
fn handle_notifications(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer();
let (tx, rx) = flume::bounded::<Event>(100);
self.tasks.push(cx.background_spawn(async move {
@@ -127,12 +130,12 @@ impl DeviceRegistry {
match event.kind {
Kind::Custom(4454) => {
if verify_author(&client, event.as_ref()).await {
if verify_author(&signer, event.as_ref()).await {
tx.send_async(event.into_owned()).await?;
}
}
Kind::Custom(4455) => {
if verify_author(&client, event.as_ref()).await {
if verify_author(&signer, event.as_ref()).await {
tx.send_async(event.into_owned()).await?;
}
}
@@ -181,7 +184,7 @@ impl DeviceRegistry {
/// Set the decoupled encryption key for the current user
fn set_signer<S>(&mut self, new: S, cx: &mut Context<Self>)
where
S: NostrSigner + 'static,
S: AsyncNostrSigner,
{
let nostr = NostrRegistry::global(cx);
let signer = nostr.read(cx).signer();
@@ -203,9 +206,10 @@ impl DeviceRegistry {
pub fn backup(&self, path: PathBuf, cx: &App) -> Task<Result<(), Error>> {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer();
cx.background_spawn(async move {
let keys = get_keys(&client).await?;
let keys = get_keys(&client, &signer).await?;
let content = keys.secret_key().to_bech32()?;
smol::fs::write(path, &content).await?;
@@ -218,9 +222,10 @@ impl DeviceRegistry {
pub fn get_announcement(&mut self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer();
let task: Task<Result<Event, Error>> = cx.background_spawn(async move {
let signer = client.signer().context("Signer not found")?;
let signer = signer.get().await;
let public_key = signer.get_public_key().await?;
// Construct the filter for the device announcement event
@@ -293,19 +298,24 @@ impl DeviceRegistry {
fn create_encryption(&self, keys: Keys, cx: &App) -> Task<Result<Keys, Error>> {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer();
let secret = keys.secret_key().to_secret_hex();
let n = keys.public_key();
cx.background_spawn(async move {
// Construct an announcement event
let builder = EventBuilder::new(Kind::Custom(10044), "").tags(vec![
Tag::custom(TagKind::custom("n"), vec![n]),
Tag::client(app_name()),
]);
// Sign the event with user's signer
let event = client.sign_event_builder(builder).await?;
let event = EventBuilder::new(Kind::Custom(10044), "")
.tags(vec![
Tag::custom("n", vec![n]),
Nip89Tag::Client {
name: app_name().to_string(),
address: None,
}
.to_tag(),
])
.sign_async(&signer.get().await)
.await?;
// Publish announcement
client
@@ -315,7 +325,7 @@ impl DeviceRegistry {
.await?;
// Save device keys to the database
set_keys(&client, &secret).await?;
set_keys(&client, &signer, &secret).await?;
Ok(keys)
})
@@ -325,13 +335,14 @@ impl DeviceRegistry {
fn set_encryption(&mut self, event: &Event, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer();
let announcement = Announcement::from(event);
let device_pubkey = announcement.public_key();
// Get encryption key from the database and compare with the announcement
let task: Task<Result<Keys, Error>> = cx.background_spawn(async move {
let keys = get_keys(&client).await?;
let keys = get_keys(&client, &signer).await?;
// Compare the public key from the announcement with the one from the database
if keys.public_key() != device_pubkey {
@@ -403,14 +414,20 @@ impl DeviceRegistry {
Some(event) => Ok(Some(event)),
// No approval event found, construct a request event
None => {
// Construct an event for device key request
let builder = EventBuilder::new(Kind::Custom(4454), "").tags(vec![
Tag::custom(TagKind::custom("P"), vec![app_pubkey]),
Tag::client(app_name()),
]);
let signer = signer.get().await;
// Sign the event with user's signer
let event = client.sign_event_builder(builder).await?;
// Construct an event for device key request
let event = EventBuilder::new(Kind::Custom(4454), "")
.tags(vec![
Tag::custom("P", vec![app_pubkey]),
Nip89Tag::Client {
name: app_name().to_string(),
address: None,
}
.to_tag(),
])
.sign_async(&signer)
.await?;
// Send the event to write relays
client.send_event(&event).to_nip65().await?;
@@ -471,17 +488,19 @@ impl DeviceRegistry {
fn extract_encryption(&mut self, event: Event, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let app_keys = nostr.read(cx).keys();
let app_signer: Arc<dyn AsyncNostrSigner> = Arc::new(app_keys);
let task: Task<Result<Keys, Error>> = cx.background_spawn(async move {
let master = event
.tags
.find(TagKind::custom("P"))
.iter()
.find(|tag| tag.kind() == "P")
.and_then(|tag| tag.content())
.and_then(|content| PublicKey::parse(content).ok())
.context("Invalid event's tags")?;
let payload = event.content.as_str();
let decrypted = app_keys.nip44_decrypt(&master, payload).await?;
let decrypted = app_signer.nip44_decrypt(&master, payload).await?;
let secret = SecretKey::from_hex(&decrypted)?;
let keys = Keys::new(secret);
@@ -510,6 +529,7 @@ impl DeviceRegistry {
fn approve(&mut self, event: &Event, window: &mut Window, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer();
// Get user's write relays
let event = event.clone();
@@ -517,31 +537,36 @@ impl DeviceRegistry {
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
// Get device keys
let keys = get_keys(&client).await?;
let keys = get_keys(&client, &signer).await?;
let public_key = keys.public_key();
let secret = keys.secret_key().to_secret_hex();
let device_signer: Arc<dyn AsyncNostrSigner> = Arc::new(keys);
let signer = signer.get().await;
// Extract the target public key from the event tags
let target = event
.tags
.find(TagKind::custom("P"))
.iter()
.find(|tag| tag.kind() == "P")
.and_then(|tag| tag.content())
.and_then(|content| PublicKey::parse(content).ok())
.context("Target is not a valid public key")?;
// Encrypt the device keys with the user's signer
let payload = keys.nip44_encrypt(&target, &secret).await?;
let payload = device_signer.nip44_encrypt(&target, &secret).await?;
// Construct the response event
//
// P tag: the current device's public key
// p tag: the requester's public key
let builder = EventBuilder::new(Kind::Custom(4455), payload).tags(vec![
Tag::custom(TagKind::custom("P"), vec![keys.public_key().to_hex()]),
Tag::public_key(target),
]);
// Sign the builder
let event = client.sign_event_builder(builder).await?;
let event = EventBuilder::new(Kind::Custom(4455), payload)
.tags(vec![
Tag::custom("P", vec![public_key.to_hex()]),
Tag::public_key(target),
])
.sign_async(&signer)
.await?;
// Send the response event to the user's relay list
client.send_event(&event).to_nip65().await?;
@@ -689,18 +714,15 @@ impl DeviceRegistry {
struct DeviceNotification;
/// Verify the author of an event
async fn verify_author(client: &Client, event: &Event) -> bool {
if let Some(signer) = client.signer()
&& let Ok(public_key) = signer.get_public_key().await
{
async fn verify_author(signer: &Arc<CoopSigner>, event: &Event) -> bool {
if let Ok(public_key) = signer.get_public_key().await {
return public_key == event.pubkey;
}
false
}
/// Encrypt and store device keys in the local database.
async fn set_keys(client: &Client, secret: &str) -> Result<(), Error> {
let signer = client.signer().context("Signer not found")?;
async fn set_keys(client: &Client, signer: &Arc<CoopSigner>, secret: &str) -> Result<(), Error> {
let public_key = signer.get_public_key().await?;
// Encrypt the value
@@ -710,7 +732,7 @@ async fn set_keys(client: &Client, secret: &str) -> Result<(), Error> {
let event = EventBuilder::new(Kind::ApplicationSpecificData, content)
.tag(Tag::identifier(IDENTIFIER))
.build(public_key)
.sign(&Keys::generate())
.sign_async(&Keys::generate())
.await?;
// Save the event to the database
@@ -720,8 +742,7 @@ async fn set_keys(client: &Client, secret: &str) -> Result<(), Error> {
}
/// Get device keys from the local database.
async fn get_keys(client: &Client) -> Result<Keys, Error> {
let signer = client.signer().context("Signer not found")?;
async fn get_keys(client: &Client, signer: &Arc<CoopSigner>) -> Result<Keys, Error> {
let public_key = signer.get_public_key().await?;
let filter = Filter::new()