use signer trait

This commit is contained in:
2026-09-30 04:51:23 +07:00
parent 7dbf55d48e
commit 44307d39f4
5 changed files with 67 additions and 47 deletions
+11 -8
View File
@@ -14,7 +14,7 @@ use gpui::{
use instant::Duration; use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP, UniversalSigner}; use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP};
mod message; mod message;
mod room; mod room;
@@ -740,11 +740,14 @@ impl ChatRegistry {
} }
/// Unwraps a gift-wrapped event and processes its contents. /// Unwraps a gift-wrapped event and processes its contents.
async fn extract_rumor( async fn extract_rumor<S>(
client: &Client, client: &Client,
signer: &UniversalSigner, signer: &S,
gift_wrap: &Event, gift_wrap: &Event,
) -> Result<UnsignedEvent, Error> { ) -> Result<UnsignedEvent, Error>
where
S: AsyncNip44 + ?Sized,
{
// Try to get cached rumor first // Try to get cached rumor first
if let Ok(rumor) = get_rumor(client, gift_wrap.id).await { if let Ok(rumor) = get_rumor(client, gift_wrap.id).await {
return Ok(rumor); return Ok(rumor);
@@ -771,10 +774,10 @@ async fn extract_rumor(
} }
/// Attempts to unwrap a gift wrap event with a given signer. /// Attempts to unwrap a gift wrap event with a given signer.
async fn try_unwrap_with( async fn try_unwrap_with<S>(signer: &S, gift_wrap: &Event) -> Result<UnwrappedGift, Error>
signer: &UniversalSigner, where
gift_wrap: &Event, S: AsyncNip44 + ?Sized,
) -> Result<UnwrappedGift, Error> { {
// Get the sealed event // Get the sealed event
let seal = signer let seal = signer
.nip44_decrypt_async(&gift_wrap.pubkey, &gift_wrap.content) .nip44_decrypt_async(&gift_wrap.pubkey, &gift_wrap.content)
+24 -21
View File
@@ -10,7 +10,7 @@ use itertools::Itertools;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use person::{Person, PersonRegistry}; use person::{Person, PersonRegistry};
use settings::{RoomConfig, SignerKind}; use settings::{RoomConfig, SignerKind};
use state::{NostrRegistry, TIMEOUT, UniversalSigner}; use state::{NostrRegistry, TIMEOUT};
use crate::{FileAttachment, KIND_FILE_MESSAGE, NewMessage}; use crate::{FileAttachment, KIND_FILE_MESSAGE, NewMessage};
@@ -473,31 +473,28 @@ impl Room {
} }
/// Select the appropriate signer based on signer kind and available keys. /// Select the appropriate signer based on signer kind and available keys.
fn select_signer( fn select_signer<'a, S>(
signer_kind: &SignerKind, signer_kind: &SignerKind,
has_announcement: bool, has_announcement: bool,
encryption_signer: &Option<UniversalSigner>, encryption_signer: &'a Option<S>,
user_signer: &UniversalSigner, user_signer: &'a S,
) -> UniversalSigner { ) -> &'a S {
match signer_kind { match signer_kind {
SignerKind::Auto => { SignerKind::Auto => {
if has_announcement { if has_announcement {
encryption_signer encryption_signer.as_ref().unwrap_or(user_signer)
.clone()
.unwrap_or_else(|| user_signer.clone())
} else { } else {
user_signer.clone() user_signer
} }
} }
SignerKind::Encryption => encryption_signer SignerKind::Encryption => encryption_signer
.clone() .as_ref()
.expect("encryption signer must be set"), .expect("encryption signer must be set"),
SignerKind::User => user_signer.clone(), SignerKind::User => user_signer,
} }
} }
/// Send the rumor to every member's messaging relays /// Send the rumor to every member's messaging relays.
/// and to the sender as a backup, when enabled.
pub fn send(&self, rumor: UnsignedEvent, cx: &App) -> Option<Task<Vec<SendReport>>> { pub fn send(&self, rumor: UnsignedEvent, cx: &App) -> Option<Task<Vec<SendReport>>> {
let config = self.config.clone(); let config = self.config.clone();
@@ -558,14 +555,17 @@ impl Room {
} }
/// Deliver a rumor to a single receiver. /// Deliver a rumor to a single receiver.
async fn deliver( async fn deliver<S>(
client: &Client, client: &Client,
signer_kind: &SignerKind, signer_kind: &SignerKind,
receiver: Person, receiver: Person,
rumor: &UnsignedEvent, rumor: &UnsignedEvent,
encryption_signer: &Option<UniversalSigner>, encryption_signer: &Option<S>,
user_signer: &UniversalSigner, user_signer: &S,
) -> SendReport { ) -> SendReport
where
S: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44,
{
let public_key = receiver.public_key(); let public_key = receiver.public_key();
let announcement = receiver.announcement(); let announcement = receiver.announcement();
@@ -595,7 +595,7 @@ async fn deliver(
user_signer, 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 { Ok(output) => SendReport {
receiver: public_key, receiver: public_key,
result: Ok(output), result: Ok(output),
@@ -608,13 +608,16 @@ async fn deliver(
} }
/// Build the gift-wrapped event for a rumor and send it. /// Build the gift-wrapped event for a rumor and send it.
async fn send_gift_wrap( async fn send_gift_wrap<S>(
client: &Client, client: &Client,
signer: &UniversalSigner, signer: &S,
receiver: &Person, receiver: &Person,
rumor: &UnsignedEvent, rumor: &UnsignedEvent,
config: &SignerKind, config: &SignerKind,
) -> Result<Output<EventId, EventSendStatus>, Error> { ) -> Result<Output<EventId, EventSendStatus>, Error>
where
S: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44,
{
let mut extra_tags = vec![Tag::custom("k", [rumor.kind.to_string()])]; let mut extra_tags = vec![Tag::custom("k", [rumor.kind.to_string()])];
// Determine the receiver public key based on the config // Determine the receiver public key based on the config
+19 -10
View File
@@ -12,7 +12,6 @@ use concord::derive::{GroupKey, channel_rekey_group_key, epoch_key_commitment};
use concord::state::{CommunityState, HeldKey}; use concord::state::{CommunityState, HeldKey};
use concord::{ChannelId, CommunityId, Epoch}; use concord::{ChannelId, CommunityId, Epoch};
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use state::UniversalSigner;
use crate::sync::{self, PlaneKind}; 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. /// Read every rekey wrap the local database holds and adopt what is admissible.
pub async fn adopt( pub async fn adopt<S>(
client: &Client, client: &Client,
state: &CommunityState, state: &CommunityState,
roles: &CommunityRoles, roles: &CommunityRoles,
signer: &UniversalSigner, signer: &S,
me: PublicKey, me: PublicKey,
) -> Result<Adoptions> { ) -> Result<Adoptions>
where
S: AsyncNip44 + ?Sized,
{
let watches = watches(state)?; let watches = watches(state)?;
let mut authors: BTreeSet<PublicKey> = BTreeSet::new(); let mut authors: BTreeSet<PublicKey> = BTreeSet::new();
@@ -362,15 +364,18 @@ impl Rewrite {
/// Publish this client's own rotation of one scope to its next epoch. /// Publish this client's own rotation of one scope to its next epoch.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub async fn rotate( pub async fn rotate<S>(
client: &Client, client: &Client,
state: &CommunityState, state: &CommunityState,
roles: &CommunityRoles, roles: &CommunityRoles,
signer: &UniversalSigner, signer: &S,
me: PublicKey, me: PublicKey,
rewrite: &Rewrite, rewrite: &Rewrite,
at: Timestamp, at: Timestamp,
) -> Result<Epoch> { ) -> Result<Epoch>
where
S: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + ?Sized,
{
if rewrite.excluded.contains(&me) { if rewrite.excluded.contains(&me) {
bail!("a rotation cannot cut off the member who publishes it"); 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. /// Walk a scope's rotations forward, one epoch at a time, off the key held.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn walk( async fn walk<S>(
scope: RekeyScope, scope: RekeyScope,
permissions: &[u64], permissions: &[u64],
mut held_epoch: Epoch, mut held_epoch: Epoch,
mut held_key: [u8; 32], mut held_key: [u8; 32],
state: &CommunityState, state: &CommunityState,
roles: &CommunityRoles, roles: &CommunityRoles,
signer: &UniversalSigner, signer: &S,
me: PublicKey, me: PublicKey,
rotations: &[Rotation], rotations: &[Rotation],
published: &BTreeMap<RotationKey, u64>, published: &BTreeMap<RotationKey, u64>,
) -> Result<Step> { ) -> Result<Step>
where
S: AsyncNip44 + ?Sized,
{
let mut step = Step::default(); let mut step = Step::default();
let mut stepped: Vec<HeldKey> = Vec::new(); let mut stepped: Vec<HeldKey> = Vec::new();
let ceiling = held_epoch.0 + REKEY_LOOKAHEAD; let ceiling = held_epoch.0 + REKEY_LOOKAHEAD;
@@ -794,6 +802,7 @@ mod tests {
}; };
use concord::state::{ChannelKeyRef, HeldRoot}; use concord::state::{ChannelKeyRef, HeldRoot};
use nostr_memory::MemoryDatabase; use nostr_memory::MemoryDatabase;
use state::UniversalSigner;
use super::*; use super::*;
+5 -6
View File
@@ -14,7 +14,6 @@ use concord::state::{CommunityState, HeldKey, HeldRoot, list_entry};
use concord::{ChannelId, CommunityId, Epoch, GroupKey, decode_hex_32}; use concord::{ChannelId, CommunityId, Epoch, GroupKey, decode_hex_32};
use gpui::AsyncApp; use gpui::AsyncApp;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use state::UniversalSigner;
use crate::cache::{self, Observed}; use crate::cache::{self, Observed};
use crate::history::{CURSOR_OVERLAP, Window}; 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 /// 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). /// as new as it, because absence from the List is never a fact (§8).
pub async fn load( pub async fn load<S>(client: &Client, signer: &S, self_pk: PublicKey) -> Result<Vec<CommunityState>>
client: &Client, where
signer: &UniversalSigner, S: AsyncGetPublicKey + AsyncNip44 + ?Sized,
self_pk: PublicKey, {
) -> Result<Vec<CommunityState>> {
let list = match load_list(client, signer, self_pk).await? { let list = match load_list(client, signer, self_pk).await? {
Some(list) => list, Some(list) => list,
None => return cache::load_states(client).await, None => return cache::load_states(client).await,
@@ -735,6 +733,7 @@ mod tests {
use std::time::Duration; use std::time::Duration;
use nostr_memory::MemoryDatabase; use nostr_memory::MemoryDatabase;
use state::UniversalSigner;
use super::*; use super::*;
+8 -2
View File
@@ -759,7 +759,10 @@ fn get_or_init_app_keys(cx: &App) -> Task<Result<Keys, Error>> {
} }
/// Encrypt and store device keys in the local database. /// 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<S>(client: &Client, signer: &S, secret: &str) -> Result<(), Error>
where
S: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + ?Sized,
{
let public_key = signer.get_public_key_async().await?; let public_key = signer.get_public_key_async().await?;
let content = signer.nip44_encrypt_async(&public_key, secret).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. /// Get device keys from the local database.
async fn get_keys(client: &Client, signer: &UniversalSigner) -> Result<Keys, Error> { async fn get_keys<S>(client: &Client, signer: &S) -> Result<Keys, Error>
where
S: AsyncGetPublicKey + AsyncNip44 + ?Sized,
{
let public_key = signer.get_public_key_async().await?; let public_key = signer.get_public_key_async().await?;
let filter = Filter::new() let filter = Filter::new()