.
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m35s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m37s
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m35s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m37s
This commit is contained in:
@@ -37,13 +37,12 @@ pub const USER_GIFTWRAP: &str = "user-gift-wraps";
|
||||
pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"];
|
||||
|
||||
/// Default search relays
|
||||
pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://relay.noswhere.com"];
|
||||
pub const SEARCH_RELAYS: [&str; 1] = ["wss://antiprimal.net"];
|
||||
|
||||
/// Default bootstrap relays
|
||||
pub const BOOTSTRAP_RELAYS: [&str; 4] = [
|
||||
pub const BOOTSTRAP_RELAYS: [&str; 3] = [
|
||||
"wss://relay.damus.io",
|
||||
"wss://relay.primal.net",
|
||||
"wss://relay.nos.social",
|
||||
"wss://user.kindpag.es",
|
||||
];
|
||||
|
||||
|
||||
@@ -48,14 +48,15 @@ pub struct NostrRegistry {
|
||||
/// Nostr client
|
||||
client: Client,
|
||||
|
||||
/// Whether the bootstrapping relays is connected
|
||||
connected: bool,
|
||||
|
||||
/// Whether coop is creating a default signer
|
||||
creating_signer: bool,
|
||||
|
||||
/// Nostr signer
|
||||
signer: Arc<CoopSigner>,
|
||||
|
||||
/// By default, Coop generates a new signer for new users.
|
||||
///
|
||||
/// This flag indicates whether the signer is user-owned or Coop-generated.
|
||||
owned_signer: bool,
|
||||
|
||||
/// NIP-65 relay state
|
||||
nip65: Entity<RelayState>,
|
||||
|
||||
@@ -129,16 +130,17 @@ impl NostrRegistry {
|
||||
cx.defer(|cx| {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
// Connect to the bootstrapping relays
|
||||
nostr.update(cx, |this, cx| {
|
||||
this.connect(cx);
|
||||
this.get_identity(cx);
|
||||
});
|
||||
});
|
||||
|
||||
Self {
|
||||
client,
|
||||
connected: false,
|
||||
creating_signer: false,
|
||||
signer,
|
||||
owned_signer: false,
|
||||
nip65,
|
||||
nip17,
|
||||
app_keys,
|
||||
@@ -151,19 +153,29 @@ impl NostrRegistry {
|
||||
fn connect(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
|
||||
self.tasks.push(cx.background_spawn(async move {
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
// Add search relay to the relay pool
|
||||
for url in SEARCH_RELAYS.into_iter() {
|
||||
client.add_relay(url).await?;
|
||||
client.add_relay(url).and_connect().await?;
|
||||
}
|
||||
|
||||
// Add bootstrap relay to the relay pool
|
||||
for url in BOOTSTRAP_RELAYS.into_iter() {
|
||||
client.add_relay(url).await?;
|
||||
client.add_relay(url).and_connect().await?;
|
||||
}
|
||||
|
||||
// Connect to all added relays
|
||||
client.connect().await;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||
// Wait for the task to complete
|
||||
task.await?;
|
||||
|
||||
// Update the state
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_connected(cx);
|
||||
this.get_signer(cx);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
@@ -174,22 +186,16 @@ impl NostrRegistry {
|
||||
self.client.clone()
|
||||
}
|
||||
|
||||
/// Get the nostr signer
|
||||
pub fn signer(&self) -> Arc<CoopSigner> {
|
||||
self.signer.clone()
|
||||
}
|
||||
|
||||
/// Get the app keys
|
||||
pub fn app_keys(&self) -> &Keys {
|
||||
&self.app_keys
|
||||
}
|
||||
|
||||
/// Returns whether the current signer is owned by user
|
||||
pub fn owned_signer(&self) -> bool {
|
||||
self.owned_signer
|
||||
}
|
||||
|
||||
/// Set whether the current signer is owned by user
|
||||
pub fn set_owned_signer(&mut self, owned: bool, cx: &mut Context<Self>) {
|
||||
self.owned_signer = owned;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Get the NIP-65 state
|
||||
pub fn nip65_state(&self) -> Entity<RelayState> {
|
||||
self.nip65.clone()
|
||||
@@ -200,16 +206,26 @@ impl NostrRegistry {
|
||||
self.nip17.clone()
|
||||
}
|
||||
|
||||
/// Get current signer's public key
|
||||
pub fn signer_pkey(&self, cx: &App) -> Task<Result<PublicKey, Error>> {
|
||||
let client = self.client();
|
||||
/// Get the connected state
|
||||
pub fn connected(&self) -> bool {
|
||||
self.connected
|
||||
}
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let signer = client.signer().context("Signer not found")?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
/// Set the connected state
|
||||
fn set_connected(&mut self, cx: &mut Context<Self>) {
|
||||
self.connected = true;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
Ok(public_key)
|
||||
})
|
||||
/// Get the creating signer status
|
||||
pub fn creating_signer(&self) -> bool {
|
||||
self.creating_signer
|
||||
}
|
||||
|
||||
/// Set the creating signer status
|
||||
fn set_creating_signer(&mut self, status: bool, cx: &mut Context<Self>) {
|
||||
self.creating_signer = status;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Get a relay hint (messaging relay) for a given public key
|
||||
@@ -290,18 +306,17 @@ impl NostrRegistry {
|
||||
T: NostrSigner + 'static,
|
||||
{
|
||||
let client = self.client();
|
||||
let signer = self.signer.clone();
|
||||
let signer = self.signer();
|
||||
|
||||
// Create a task to update the signer and verify the public key
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
// Update signer
|
||||
signer.switch(new).await;
|
||||
signer.switch(new, owned).await;
|
||||
|
||||
// Verify signer
|
||||
let signer = client.signer().context("Signer not found")?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
|
||||
log::info!("Signer's public key: {public_key}");
|
||||
log::info!("Signer's public key: {}", public_key);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
@@ -313,8 +328,6 @@ impl NostrRegistry {
|
||||
// Update states
|
||||
this.update(cx, |this, cx| {
|
||||
this.reset_relay_states(cx);
|
||||
this.get_relay_list(cx);
|
||||
this.set_owned_signer(owned, cx);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
@@ -615,83 +628,61 @@ impl NostrRegistry {
|
||||
})
|
||||
}
|
||||
|
||||
/// Get local stored identity
|
||||
fn get_identity(&mut self, cx: &mut Context<Self>) {
|
||||
let read_credential = cx.read_credentials(KEYRING);
|
||||
|
||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||
match read_credential.await {
|
||||
Ok(Some((_, secret))) => {
|
||||
let secret = SecretKey::from_slice(&secret)?;
|
||||
let keys = Keys::new(secret);
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_signer(keys, false, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
_ => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.get_bunker(cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
/// Create a new identity
|
||||
fn create_identity(&mut self, cx: &mut Context<Self>) {
|
||||
fn set_default_signer(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
let keys = Keys::generate();
|
||||
let async_keys = keys.clone();
|
||||
|
||||
// Get write credential task
|
||||
// Create a write credential task
|
||||
let write_credential = cx.write_credentials(
|
||||
KEYRING,
|
||||
&keys.public_key().to_hex(),
|
||||
&keys.secret_key().to_secret_bytes(),
|
||||
);
|
||||
|
||||
// Update the signer
|
||||
self.set_signer(keys, false, cx);
|
||||
|
||||
// Set the creating signer status
|
||||
self.set_creating_signer(true, cx);
|
||||
|
||||
// Run async tasks in background
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
// Build and sign the relay list event
|
||||
let signer = client.signer().context("Signer not found")?;
|
||||
|
||||
// Get default relay list
|
||||
let relay_list = default_relay_list();
|
||||
let event = EventBuilder::relay_list(relay_list)
|
||||
.sign(&async_keys)
|
||||
.await?;
|
||||
|
||||
// Publish relay list event
|
||||
client.send_event(&event).await?;
|
||||
let event = EventBuilder::relay_list(relay_list).sign(signer).await?;
|
||||
let output = client.send_event(&event).broadcast().await?;
|
||||
log::info!("Published relay list event: {:?}", output.id());
|
||||
|
||||
// Build and sign the metadata event
|
||||
// Construct the default metadata
|
||||
let name = petname::petname(2, "-").unwrap_or("Cooper".to_string());
|
||||
let avatar = Url::parse(&format!("https://avatar.vercel.sh/{name}")).unwrap();
|
||||
let metadata = Metadata::new().display_name(&name).picture(avatar);
|
||||
let event = EventBuilder::metadata(&metadata).sign(&async_keys).await?;
|
||||
|
||||
// Publish metadata event
|
||||
client.send_event(&event).await?;
|
||||
let event = EventBuilder::metadata(&metadata).sign(signer).await?;
|
||||
let output = client.send_event(&event).broadcast().await?;
|
||||
log::info!("Published metadata event: {:?}", output.id());
|
||||
|
||||
// Build and sign the contact list event
|
||||
// Construct the default contact list
|
||||
let contacts = vec![Contact::new(PublicKey::parse(COOP_PUBKEY).unwrap())];
|
||||
let event = EventBuilder::contact_list(contacts)
|
||||
.sign(&async_keys)
|
||||
.await?;
|
||||
|
||||
// Publish contact list event
|
||||
client.send_event(&event).await?;
|
||||
let event = EventBuilder::contact_list(contacts).sign(signer).await?;
|
||||
let output = client.send_event(&event).broadcast().await?;
|
||||
log::info!("Published contact list event: {:?}", output.id());
|
||||
|
||||
// Build and sign the messaging relay list event
|
||||
// Construct the default messaging relay list
|
||||
let relays = default_messaging_relays();
|
||||
let event = EventBuilder::nip17_relay_list(relays)
|
||||
.sign(&async_keys)
|
||||
.await?;
|
||||
|
||||
// Publish messaging relay list event
|
||||
client.send_event(&event).await?;
|
||||
let event = EventBuilder::nip17_relay_list(relays).sign(signer).await?;
|
||||
let output = client.send_event(&event).to_nip65().await?;
|
||||
log::info!("Published messaging relay list event: {:?}", output.id());
|
||||
|
||||
// Write user's credentials to the system keyring
|
||||
write_credential.await?;
|
||||
@@ -703,15 +694,41 @@ impl NostrRegistry {
|
||||
// Wait for the task to complete
|
||||
task.await?;
|
||||
|
||||
// Update the signer
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_signer(keys, false, cx);
|
||||
this.set_creating_signer(false, cx);
|
||||
this.get_relay_list(cx);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
/// Get local stored signer
|
||||
fn get_signer(&mut self, cx: &mut Context<Self>) {
|
||||
let read_credential = cx.read_credentials(KEYRING);
|
||||
|
||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||
match read_credential.await {
|
||||
Ok(Some((_user, secret))) => {
|
||||
let secret = SecretKey::from_slice(&secret)?;
|
||||
let keys = Keys::new(secret);
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_signer(keys, false, cx);
|
||||
this.get_relay_list(cx);
|
||||
})?;
|
||||
}
|
||||
_ => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.get_bunker(cx);
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
/// Get local stored bunker connection
|
||||
fn get_bunker(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
@@ -741,6 +758,7 @@ impl NostrRegistry {
|
||||
Ok(signer) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_signer(signer, true, cx);
|
||||
this.get_relay_list(cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
@@ -748,7 +766,7 @@ impl NostrRegistry {
|
||||
log::warn!("Failed to get bunker: {e}");
|
||||
// Create a new identity if no stored bunker exists
|
||||
this.update(cx, |this, cx| {
|
||||
this.create_identity(cx);
|
||||
this.set_default_signer(cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
use std::result::Result;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use nostr_sdk::prelude::*;
|
||||
@@ -8,6 +9,17 @@ use smol::lock::RwLock;
|
||||
#[derive(Debug)]
|
||||
pub struct CoopSigner {
|
||||
signer: RwLock<Arc<dyn NostrSigner>>,
|
||||
|
||||
/// Signer's public key
|
||||
signer_pkey: RwLock<Option<PublicKey>>,
|
||||
|
||||
/// Whether coop is creating a new identity
|
||||
creating: AtomicBool,
|
||||
|
||||
/// By default, Coop generates a new signer for new users.
|
||||
///
|
||||
/// This flag indicates whether the signer is user-owned or Coop-generated.
|
||||
owned: AtomicBool,
|
||||
}
|
||||
|
||||
impl CoopSigner {
|
||||
@@ -17,6 +29,9 @@ impl CoopSigner {
|
||||
{
|
||||
Self {
|
||||
signer: RwLock::new(signer.into_nostr_signer()),
|
||||
signer_pkey: RwLock::new(None),
|
||||
creating: AtomicBool::new(false),
|
||||
owned: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +40,39 @@ impl CoopSigner {
|
||||
self.signer.read().await.clone()
|
||||
}
|
||||
|
||||
/// Get public key
|
||||
pub fn public_key(&self) -> Option<PublicKey> {
|
||||
self.signer_pkey.read_blocking().to_owned()
|
||||
}
|
||||
|
||||
/// Get the flag indicating whether the signer is creating a new identity.
|
||||
pub fn creating(&self) -> bool {
|
||||
self.creating.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Get the flag indicating whether the signer is user-owned.
|
||||
pub fn owned(&self) -> bool {
|
||||
self.owned.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Switch the current signer to a new signer.
|
||||
pub async fn switch<T>(&self, new: T)
|
||||
pub async fn switch<T>(&self, new: T, owned: bool)
|
||||
where
|
||||
T: IntoNostrSigner,
|
||||
{
|
||||
let new_signer = new.into_nostr_signer();
|
||||
let public_key = new_signer.get_public_key().await.ok();
|
||||
let mut signer = self.signer.write().await;
|
||||
*signer = new.into_nostr_signer();
|
||||
let mut signer_pkey = self.signer_pkey.write().await;
|
||||
|
||||
// Switch to the new signer
|
||||
*signer = new_signer;
|
||||
|
||||
// Update the public key
|
||||
*signer_pkey = public_key;
|
||||
|
||||
// Update the owned flag
|
||||
self.owned.store(owned, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user