Skip to content

Commit

Permalink
connect: refactor NostrConnectRemoteSigner to use synchronous const…
Browse files Browse the repository at this point in the history
…ructors

Signed-off-by: Yuki Kishimoto <[email protected]>
  • Loading branch information
yukibtc committed Nov 25, 2024
1 parent 29cb2d2 commit 0da9c85
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 48 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* nostr: change `TagStandard::Relay` variant inner type ([Yuki Kishimoto])
* pool: switch from async to sync message sending for `Relay` ([Yuki Kishimoto])
* connect: refactor `NostrConnectRemoteSigner` to use distinct keys for signer and user ([Yuki Kishimoto])
* connect: refactor `NostrConnectRemoteSigner` to use synchronous constructors ([Yuki Kishimoto])
* sdk: disable all default features ([Yuki Kishimoto])
* sdk: set `Client::from_builder` as private ([Yuki Kishimoto])
* ffi: convert `NostrSigner` trait to an object ([Yuki Kishimoto])
Expand Down
24 changes: 8 additions & 16 deletions bindings/nostr-sdk-ffi/src/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,8 @@ pub struct NostrConnectRemoteSigner {

#[uniffi::export(async_runtime = "tokio")]
impl NostrConnectRemoteSigner {
// TODO: change again to `new` (currently python not support async constructor)
#[uniffi::constructor(default(secret = None, opts = None))]
pub async fn init(
pub fn new(
keys: NostrConnectKeys,
relays: Vec<String>,
secret: Option<String>,
Expand All @@ -155,14 +154,13 @@ impl NostrConnectRemoteSigner {
relays,
secret,
opts.map(|o| o.as_ref().deref().clone()),
)
.await?,
)?,
})
}

/// Construct remote signer from client URI (`nostrconnect://..`)
#[uniffi::constructor(default(secret = None, opts = None))]
pub async fn from_uri(
pub fn from_uri(
uri: &NostrConnectURI,
keys: NostrConnectKeys,
secret: Option<String>,
Expand All @@ -174,24 +172,18 @@ impl NostrConnectRemoteSigner {
keys.into(),
secret,
opts.map(|o| o.as_ref().deref().clone()),
)
.await?,
)?,
})
}

/// Get signer relays
pub async fn relays(&self) -> Vec<String> {
self.inner
.relays()
.await
.into_iter()
.map(|r| r.to_string())
.collect()
pub fn relays(&self) -> Vec<String> {
self.inner.relays().iter().map(|r| r.to_string()).collect()
}

/// Get `bunker` URI
pub async fn bunker_uri(&self) -> NostrConnectURI {
self.inner.bunker_uri().await.into()
pub fn bunker_uri(&self) -> NostrConnectURI {
self.inner.bunker_uri().into()
}

/// Serve signer
Expand Down
9 changes: 3 additions & 6 deletions crates/nostr-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,13 @@ async fn run() -> Result<()> {
let signer: NostrConnectRemoteSigner = match uri {
Some(uri) => {
let uri: NostrConnectURI = NostrConnectURI::parse(&uri)?;
NostrConnectRemoteSigner::from_uri(uri, keys, None, None).await?
}
None => {
NostrConnectRemoteSigner::new(keys, ["wss://relay.nsec.app"], None, None)
.await?
NostrConnectRemoteSigner::from_uri(uri, keys, None, None)?
}
None => NostrConnectRemoteSigner::new(keys, ["wss://relay.nsec.app"], None, None)?,
};

// Print bunker URI
let uri: NostrConnectURI = signer.bunker_uri().await;
let uri: NostrConnectURI = signer.bunker_uri();
println!("\nBunker URI: {uri}\n");

// Serve signer
Expand Down
6 changes: 3 additions & 3 deletions crates/nostr-connect/examples/nostr-connect-signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ async fn main() -> Result<()> {
};

// Compose signer
let signer = NostrConnectRemoteSigner::new(keys, ["wss://relay.nsec.app"], None, None).await?;
let signer = NostrConnectRemoteSigner::new(keys, ["wss://relay.nsec.app"], None, None)?;

// Compose signer from URI
// let uri = NostrConnectURI::parse("nostrconnect://...")?;
// let signer = NostrConnectRemoteSigner::from_uri(uri, keys, None, None).await?;
// let signer = NostrConnectRemoteSigner::from_uri(uri, keys, None, None)?;

// Print bunker URI
let uri = signer.bunker_uri().await;
let uri = signer.bunker_uri();
println!("\n{uri}\n");

// Serve signer
Expand Down
5 changes: 5 additions & 0 deletions crates/nostr-connect/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

//! Nostr Connect error
use std::convert::Infallible;

use nostr::event::builder;
use nostr::nips::{nip04, nip46};
use nostr::PublicKey;
Expand Down Expand Up @@ -52,4 +54,7 @@ pub enum Error {
/// The local set user public key
local: Box<PublicKey>,
},
/// Infallible
#[error(transparent)]
Infallible(#[from] Infallible),
}
78 changes: 55 additions & 23 deletions crates/nostr-connect/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

//! Nostr Connect signer
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;

use nostr::nips::nip46::{Message, Request, ResponseResult};
Expand All @@ -30,15 +32,19 @@ pub struct NostrConnectKeys {
#[derive(Debug, Clone)]
pub struct NostrConnectRemoteSigner {
keys: NostrConnectKeys,
relays: Vec<Url>,
pool: RelayPool,
opts: RelayOptions,
secret: Option<String>,
nostr_connect_client_public_key: Option<PublicKey>,
bootstrapped: Arc<AtomicBool>,
}

impl NostrConnectRemoteSigner {
/// Construct new remote signer
pub async fn new<I, U>(
pub fn new<I, U>(
keys: NostrConnectKeys,
relays: I,
urls: I,
secret: Option<String>,
opts: Option<RelayOptions>,
) -> Result<Self, Error>
Expand All @@ -47,21 +53,28 @@ impl NostrConnectRemoteSigner {
U: TryIntoUrl,
pool::Error: From<<U as TryIntoUrl>::Err>,
{
// Compose pool
let pool: RelayPool = RelayPool::default();

let opts: RelayOptions = opts.unwrap_or_default();
for url in relays.into_iter() {
pool.add_relay(url, opts.clone()).await?;
let mut relays = Vec::new();
for relay in urls.into_iter() {
relays.push(
relay
.try_into_url()
.map_err(|e| Error::Pool(pool::Error::from(e)))?,
);
}

pool.connect(Some(Duration::from_secs(10))).await;

Ok(Self { keys, pool, secret })
Ok(Self {
keys,
relays,
pool: RelayPool::default(),
opts: opts.unwrap_or_default(),
secret,
nostr_connect_client_public_key: None,
bootstrapped: Arc::new(AtomicBool::new(false)),
})
}

/// Construct remote signer from client URI (`nostrconnect://..`)
pub async fn from_uri(
pub fn from_uri(
uri: NostrConnectURI,
keys: NostrConnectKeys,
secret: Option<String>,
Expand All @@ -71,24 +84,24 @@ impl NostrConnectRemoteSigner {
NostrConnectURI::Client {
public_key, relays, ..
} => {
let this = Self::new(keys, relays, secret, opts).await?;
this.send_connect_ack(public_key).await?;
Ok(this)
let mut signer = Self::new(keys, relays, secret, opts)?;
signer.nostr_connect_client_public_key = Some(public_key);
Ok(signer)
}
NostrConnectURI::Bunker { .. } => Err(Error::UnexpectedUri),
}
}

/// Get signer relays
pub async fn relays(&self) -> Vec<Url> {
self.pool.relays().await.into_keys().collect()
pub fn relays(&self) -> &[Url] {
&self.relays
}

/// Get `bunker` URI
pub async fn bunker_uri(&self) -> NostrConnectURI {
pub fn bunker_uri(&self) -> NostrConnectURI {
NostrConnectURI::Bunker {
remote_signer_public_key: self.keys.signer.public_key(),
relays: self.relays().await,
relays: self.relays().to_vec(),
secret: self.secret.clone(),
}
}
Expand All @@ -104,11 +117,22 @@ impl NostrConnectRemoteSigner {
Ok(())
}

async fn subscribe(&self) -> Result<(), Error> {
let public_key: PublicKey = self.keys.signer.public_key();
async fn bootstrap(&self) -> Result<(), Error> {
// Check if already bootstrapped
if self.bootstrapped.load(Ordering::SeqCst) {
return Ok(());
}

// Add relays to pool
for url in self.relays.iter() {
self.pool.add_relay(url, self.opts.clone()).await?;
}

// Connect
self.pool.connect(Some(Duration::from_secs(10))).await;

let filter = Filter::new()
.pubkey(public_key)
.pubkey(self.keys.signer.public_key())
.kind(Kind::NostrConnect)
.since(Timestamp::now());

Expand All @@ -117,6 +141,9 @@ impl NostrConnectRemoteSigner {
.subscribe(vec![filter], SubscribeOptions::default())
.await?;

// Mark as bootstrapped
self.bootstrapped.store(true, Ordering::SeqCst);

Ok(())
}

Expand All @@ -125,7 +152,12 @@ impl NostrConnectRemoteSigner {
where
T: NostrConnectSignerActions,
{
self.subscribe().await?;
self.bootstrap().await?;

// TODO: move into bootstrap method?
if let Some(public_key) = self.nostr_connect_client_public_key {
self.send_connect_ack(public_key).await?;
}

self.pool
.handle_notifications(|notification| async {
Expand Down

0 comments on commit 0da9c85

Please sign in to comment.