From 44307d39f48ed545bc4cf34fad25d5fb81bf5d40 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Wed, 30 Sep 2026 04:51:23 +0700 Subject: [PATCH] use signer trait --- crates/chat/src/lib.rs | 19 ++++++++------- crates/chat/src/room.rs | 45 +++++++++++++++++++---------------- crates/community/src/rekey.rs | 29 ++++++++++++++-------- crates/community/src/sync.rs | 11 ++++----- crates/device/src/lib.rs | 10 ++++++-- 5 files changed, 67 insertions(+), 47 deletions(-) diff --git a/crates/chat/src/lib.rs b/crates/chat/src/lib.rs index c35b7143..1d3a3cf2 100644 --- a/crates/chat/src/lib.rs +++ b/crates/chat/src/lib.rs @@ -14,7 +14,7 @@ use gpui::{ use instant::Duration; use nostr_sdk::prelude::*; use smallvec::{SmallVec, smallvec}; -use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP, UniversalSigner}; +use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP}; mod message; mod room; @@ -740,11 +740,14 @@ impl ChatRegistry { } /// Unwraps a gift-wrapped event and processes its contents. -async fn extract_rumor( +async fn extract_rumor( client: &Client, - signer: &UniversalSigner, + signer: &S, gift_wrap: &Event, -) -> Result { +) -> Result +where + S: AsyncNip44 + ?Sized, +{ // Try to get cached rumor first if let Ok(rumor) = get_rumor(client, gift_wrap.id).await { return Ok(rumor); @@ -771,10 +774,10 @@ async fn extract_rumor( } /// Attempts to unwrap a gift wrap event with a given signer. -async fn try_unwrap_with( - signer: &UniversalSigner, - gift_wrap: &Event, -) -> Result { +async fn try_unwrap_with(signer: &S, gift_wrap: &Event) -> Result +where + S: AsyncNip44 + ?Sized, +{ // Get the sealed event let seal = signer .nip44_decrypt_async(&gift_wrap.pubkey, &gift_wrap.content) diff --git a/crates/chat/src/room.rs b/crates/chat/src/room.rs index 74ba9a66..00bd2294 100644 --- a/crates/chat/src/room.rs +++ b/crates/chat/src/room.rs @@ -10,7 +10,7 @@ use itertools::Itertools; use nostr_sdk::prelude::*; use person::{Person, PersonRegistry}; use settings::{RoomConfig, SignerKind}; -use state::{NostrRegistry, TIMEOUT, UniversalSigner}; +use state::{NostrRegistry, TIMEOUT}; use crate::{FileAttachment, KIND_FILE_MESSAGE, NewMessage}; @@ -473,31 +473,28 @@ impl Room { } /// Select the appropriate signer based on signer kind and available keys. - fn select_signer( + fn select_signer<'a, S>( signer_kind: &SignerKind, has_announcement: bool, - encryption_signer: &Option, - user_signer: &UniversalSigner, - ) -> UniversalSigner { + encryption_signer: &'a Option, + user_signer: &'a S, + ) -> &'a S { match signer_kind { SignerKind::Auto => { if has_announcement { - encryption_signer - .clone() - .unwrap_or_else(|| user_signer.clone()) + encryption_signer.as_ref().unwrap_or(user_signer) } else { - user_signer.clone() + user_signer } } SignerKind::Encryption => encryption_signer - .clone() + .as_ref() .expect("encryption signer must be set"), - SignerKind::User => user_signer.clone(), + SignerKind::User => user_signer, } } - /// Send the rumor to every member's messaging relays - /// and to the sender as a backup, when enabled. + /// Send the rumor to every member's messaging relays. pub fn send(&self, rumor: UnsignedEvent, cx: &App) -> Option>> { let config = self.config.clone(); @@ -558,14 +555,17 @@ impl Room { } /// Deliver a rumor to a single receiver. -async fn deliver( +async fn deliver( client: &Client, signer_kind: &SignerKind, receiver: Person, rumor: &UnsignedEvent, - encryption_signer: &Option, - user_signer: &UniversalSigner, -) -> SendReport { + encryption_signer: &Option, + user_signer: &S, +) -> SendReport +where + S: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44, +{ let public_key = receiver.public_key(); let announcement = receiver.announcement(); @@ -595,7 +595,7 @@ async fn deliver( user_signer, ); - match send_gift_wrap(client, &signer, &receiver, rumor, signer_kind).await { + match send_gift_wrap(client, signer, &receiver, rumor, signer_kind).await { Ok(output) => SendReport { receiver: public_key, result: Ok(output), @@ -608,13 +608,16 @@ async fn deliver( } /// Build the gift-wrapped event for a rumor and send it. -async fn send_gift_wrap( +async fn send_gift_wrap( client: &Client, - signer: &UniversalSigner, + signer: &S, receiver: &Person, rumor: &UnsignedEvent, config: &SignerKind, -) -> Result, Error> { +) -> Result, Error> +where + S: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44, +{ let mut extra_tags = vec![Tag::custom("k", [rumor.kind.to_string()])]; // Determine the receiver public key based on the config diff --git a/crates/community/src/rekey.rs b/crates/community/src/rekey.rs index 787c3777..42891241 100644 --- a/crates/community/src/rekey.rs +++ b/crates/community/src/rekey.rs @@ -12,7 +12,6 @@ use concord::derive::{GroupKey, channel_rekey_group_key, epoch_key_commitment}; use concord::state::{CommunityState, HeldKey}; use concord::{ChannelId, CommunityId, Epoch}; use nostr_sdk::prelude::*; -use state::UniversalSigner; use crate::sync::{self, PlaneKind}; @@ -175,13 +174,16 @@ pub struct ChannelAdoption { } /// Read every rekey wrap the local database holds and adopt what is admissible. -pub async fn adopt( +pub async fn adopt( client: &Client, state: &CommunityState, roles: &CommunityRoles, - signer: &UniversalSigner, + signer: &S, me: PublicKey, -) -> Result { +) -> Result +where + S: AsyncNip44 + ?Sized, +{ let watches = watches(state)?; let mut authors: BTreeSet = BTreeSet::new(); @@ -362,15 +364,18 @@ impl Rewrite { /// Publish this client's own rotation of one scope to its next epoch. #[allow(clippy::too_many_arguments)] -pub async fn rotate( +pub async fn rotate( client: &Client, state: &CommunityState, roles: &CommunityRoles, - signer: &UniversalSigner, + signer: &S, me: PublicKey, rewrite: &Rewrite, at: Timestamp, -) -> Result { +) -> Result +where + S: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + ?Sized, +{ if rewrite.excluded.contains(&me) { bail!("a rotation cannot cut off the member who publishes it"); } @@ -611,18 +616,21 @@ struct Delivery { /// Walk a scope's rotations forward, one epoch at a time, off the key held. #[allow(clippy::too_many_arguments)] -async fn walk( +async fn walk( scope: RekeyScope, permissions: &[u64], mut held_epoch: Epoch, mut held_key: [u8; 32], state: &CommunityState, roles: &CommunityRoles, - signer: &UniversalSigner, + signer: &S, me: PublicKey, rotations: &[Rotation], published: &BTreeMap, -) -> Result { +) -> Result +where + S: AsyncNip44 + ?Sized, +{ let mut step = Step::default(); let mut stepped: Vec = Vec::new(); let ceiling = held_epoch.0 + REKEY_LOOKAHEAD; @@ -794,6 +802,7 @@ mod tests { }; use concord::state::{ChannelKeyRef, HeldRoot}; use nostr_memory::MemoryDatabase; + use state::UniversalSigner; use super::*; diff --git a/crates/community/src/sync.rs b/crates/community/src/sync.rs index f8d944c7..edfe79e5 100644 --- a/crates/community/src/sync.rs +++ b/crates/community/src/sync.rs @@ -14,7 +14,6 @@ use concord::state::{CommunityState, HeldKey, HeldRoot, list_entry}; use concord::{ChannelId, CommunityId, Epoch, GroupKey, decode_hex_32}; use gpui::AsyncApp; use nostr_sdk::prelude::*; -use state::UniversalSigner; use crate::cache::{self, Observed}; use crate::history::{CURSOR_OVERLAP, Window}; @@ -348,11 +347,10 @@ pub async fn subscribe_list(client: &Client, self_pk: PublicKey) -> Result<()> { /// /// A held membership is dropped only when the List carries a tombstone at least /// as new as it, because absence from the List is never a fact (ยง8). -pub async fn load( - client: &Client, - signer: &UniversalSigner, - self_pk: PublicKey, -) -> Result> { +pub async fn load(client: &Client, signer: &S, self_pk: PublicKey) -> Result> +where + S: AsyncGetPublicKey + AsyncNip44 + ?Sized, +{ let list = match load_list(client, signer, self_pk).await? { Some(list) => list, None => return cache::load_states(client).await, @@ -735,6 +733,7 @@ mod tests { use std::time::Duration; use nostr_memory::MemoryDatabase; + use state::UniversalSigner; use super::*; diff --git a/crates/device/src/lib.rs b/crates/device/src/lib.rs index 3bca1745..d74bc577 100644 --- a/crates/device/src/lib.rs +++ b/crates/device/src/lib.rs @@ -759,7 +759,10 @@ fn get_or_init_app_keys(cx: &App) -> Task> { } /// Encrypt and store device keys in the local database. -async fn set_keys(client: &Client, signer: &UniversalSigner, secret: &str) -> Result<(), Error> { +async fn set_keys(client: &Client, signer: &S, secret: &str) -> Result<(), Error> +where + S: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + ?Sized, +{ let public_key = signer.get_public_key_async().await?; let content = signer.nip44_encrypt_async(&public_key, secret).await?; @@ -776,7 +779,10 @@ async fn set_keys(client: &Client, signer: &UniversalSigner, secret: &str) -> Re } /// Get device keys from the local database. -async fn get_keys(client: &Client, signer: &UniversalSigner) -> Result { +async fn get_keys(client: &Client, signer: &S) -> Result +where + S: AsyncGetPublicKey + AsyncNip44 + ?Sized, +{ let public_key = signer.get_public_key_async().await?; let filter = Filter::new()