Skip to content

Commit

Permalink
Merge pull request #1125 from MutinyWallet/unify-relays
Browse files Browse the repository at this point in the history
Better setup for new profiles
  • Loading branch information
benthecarman authored Apr 11, 2024
2 parents 99ae803 + 84c82f6 commit 653a196
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 27 deletions.
9 changes: 2 additions & 7 deletions mutiny-core/src/hermes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use url::Url;

use crate::event::{HTLCStatus, MillisatAmount, PaymentInfo};
use crate::labels::LabelStorage;
use crate::nostr::RELAYS;
use crate::storage::persist_payment_info;
use crate::{
blindauth::{BlindAuthClient, SignedToken},
Expand Down Expand Up @@ -94,14 +95,8 @@ impl<S: MutinyStorage> HermesClient<S> {
let public_key = keys.public_key();
let client = Client::new(&keys);

let relays: Vec<String> = vec![
"wss://relay.primal.net".to_string(),
"wss://relay.damus.io".to_string(),
"wss://nostr.mutinywallet.com".to_string(),
"wss://relay.mutinywallet.com".to_string(),
];
client
.add_relays(relays)
.add_relays(RELAYS)
.await
.expect("Failed to add relays");

Expand Down
9 changes: 3 additions & 6 deletions mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ use std::{str::FromStr, sync::atomic::Ordering};
use uuid::Uuid;

use crate::labels::LabelItem;
use crate::nostr::NostrKeySource;
use crate::nostr::{NostrKeySource, RELAYS};
#[cfg(test)]
use mockall::{automock, predicate::*};

Expand Down Expand Up @@ -2165,7 +2165,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
async fn sync_nostr_profile(&self) -> Result<(), MutinyError> {
let npub = self.nostr.get_npub().await;
if let Some(metadata) = self.nostr.primal_client.get_user_profile(npub).await? {
self.storage.set_nostr_profile(metadata)?;
self.storage.set_nostr_profile(&metadata)?;
}

Ok(())
Expand Down Expand Up @@ -2588,10 +2588,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
Some(zap_npub) => {
let data = ZapRequestData {
public_key: zap_npub,
relays: vec![
"wss://relay.mutinywallet.com".into(),
"wss://relay.primal.net".into(),
],
relays: RELAYS.iter().map(|r| (*r).into()).collect(),
message: comment.unwrap_or_default(),
amount: Some(msats),
lnurl: Some(lnurl.encode()),
Expand Down
130 changes: 117 additions & 13 deletions mutiny-core/src/nostr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ use nostr::nips::nip47::*;
use nostr::{
nips::nip04::{decrypt, encrypt},
Alphabet, Event, EventBuilder, EventId, Filter, JsonUtil, Keys, Kind, Metadata, SecretKey,
SingleLetterTag, Tag, TagKind, Timestamp,
SingleLetterTag, Tag, TagKind, Timestamp, UncheckedUrl,
};
use nostr_sdk::{Client, NostrSigner, RelayPoolNotification};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::collections::{HashMap, HashSet};
use std::sync::{atomic::Ordering, Arc, RwLock};
use std::time::Duration;
Expand Down Expand Up @@ -64,6 +65,14 @@ pub enum ReservedProfile {

pub(crate) const MUTINY_PLUS_SUBSCRIPTION_LABEL: &str = "Mutiny+ Subscription";

/// Default relays to use for nostr
pub(crate) const RELAYS: [&str; 4] = [
"wss://relay.primal.net",
"wss://relay.damus.io",
"wss://nostr.mutinywallet.com",
"wss://relay.mutinywallet.com",
];

impl ReservedProfile {
pub fn info(&self) -> (&'static str, u32) {
let (n, i) = match self {
Expand Down Expand Up @@ -265,16 +274,9 @@ impl<S: MutinyStorage> NostrManager<S> {
.iter()
.filter(|x| x.profile.active())
.map(|x| x.profile.relay.clone())
.chain(RELAYS.iter().map(|x| x.to_string()))
.collect();

// add relays to pull DMs from
relays.push("wss://relay.primal.net".to_string());
relays.push("wss://relay.damus.io".to_string());

// add relays for default sending
relays.push("wss://nostr.mutinywallet.com".to_string()); // blastr
relays.push("wss://relay.mutinywallet.com".to_string()); // strfry

// remove duplicates
relays.sort();
relays.dedup();
Expand Down Expand Up @@ -393,7 +395,7 @@ impl<S: MutinyStorage> NostrManager<S> {
};

let with_name = if let Some(name) = name {
current.name(name)
current.name(name.clone()).display_name(name)
} else {
current
};
Expand All @@ -419,11 +421,98 @@ impl<S: MutinyStorage> NostrManager<S> {

let event_id = self.client.set_metadata(&with_nip05).await?;
log_info!(self.logger, "New kind 0: {event_id}");
self.storage.set_nostr_profile(with_nip05.clone())?;
self.storage.set_nostr_profile(&with_nip05)?;

Ok(with_nip05)
}

/// This should only be called when the user is setting up a new profile
/// never for an existing profile
pub async fn setup_new_profile(
&self,
name: Option<String>,
img_url: Option<Url>,
lnurl: Option<LnUrl>,
nip05: Option<String>,
) -> Result<Metadata, MutinyError> {
// pull latest profile from primal
let npub = self.get_npub().await;
match self.primal_client.get_user_profile(npub).await {
Ok(Some(_)) => {
log_error!(
self.logger,
"User already has a profile, can't setup new profile"
);
return Err(MutinyError::InvalidArgumentsError);
}
Ok(None) => (),
Err(e) => {
// if we can't get the profile from primal, fall back to local
// otherwise we can't create profile if the primal server is down
log_error!(
self.logger,
"Failed to get user profile from primal, falling back to local: {e}"
);
if self.storage.get_nostr_profile()?.is_some() {
return Err(MutinyError::InvalidArgumentsError);
}
}
};

// create follow/relays list
{
// make sure we follow ourselves as well, makes other nostr feeds better
let tags = [Tag::public_key(npub)];
// set relay list in the content properly
let content: HashMap<String, Value> = RELAYS
.iter()
.map(|relay| {
let value = json!({"read":true,"write":true});
(relay.to_string(), value)
})
.collect();
let builder = EventBuilder::new(Kind::ContactList, json!(content).to_string(), tags);
self.client.send_event_builder(builder).await?;
}

// create real relay list
{
let builder = EventBuilder::relay_list(
RELAYS
.iter()
.map(|x| (UncheckedUrl::from(x.to_string()), None)),
);
self.client.send_event_builder(builder).await?;
}

// create metadata
let metadata = {
let name = name.unwrap_or_else(|| "Anon".to_string());
let mut m = Metadata::default().name(name.clone()).display_name(name);

if let Some(img_url) = img_url {
m = m.picture(img_url)
};
if let Some(lnurl) = lnurl {
if let Some(ln_addr) = lnurl.lightning_address() {
m = m.lud16(ln_addr.to_string())
} else {
m = m.lud06(lnurl.to_string())
}
};
if let Some(nip05) = nip05 {
m = m.nip05(nip05)
};
m
};

let event_id = self.client.set_metadata(&metadata).await?;
log_info!(self.logger, "New kind 0: {event_id}");
self.storage.set_nostr_profile(&metadata)?;

Ok(metadata)
}

/// Sets the user's nostr profile metadata as deleted
pub async fn delete_profile(&self) -> Result<Metadata, MutinyError> {
let metadata = Metadata::default()
Expand All @@ -434,7 +523,7 @@ impl<S: MutinyStorage> NostrManager<S> {

let event_id = self.client.set_metadata(&metadata).await?;
log_info!(self.logger, "New kind 0: {event_id}");
self.storage.set_nostr_profile(metadata.clone())?;
self.storage.set_nostr_profile(&metadata)?;

Ok(metadata)
}
Expand Down Expand Up @@ -480,7 +569,22 @@ impl<S: MutinyStorage> NostrManager<S> {
tags.push(Tag::public_key(npub));
EventBuilder::new(Kind::ContactList, content, tags)
}
None => EventBuilder::new(Kind::ContactList, "", [Tag::public_key(npub)]),
None => {
// make sure we follow ourselves as well, makes other nostr feeds better
let tags = [
Tag::public_key(npub),
Tag::public_key(self.get_npub().await),
];
// set relay list in the content properly
let content: HashMap<String, Value> = RELAYS
.iter()
.map(|relay| {
let value = json!({"read":true,"write":true});
(relay.to_string(), value)
})
.collect();
EventBuilder::new(Kind::ContactList, json!(content).to_string(), tags)
}
};

let event = self
Expand Down
2 changes: 1 addition & 1 deletion mutiny-core/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ pub trait MutinyStorage: Clone + Sized + Send + Sync + 'static {
self.get_data(NOSTR_PROFILE_METADATA)
}

fn set_nostr_profile(&self, metadata: Metadata) -> Result<(), MutinyError> {
fn set_nostr_profile(&self, metadata: &Metadata) -> Result<(), MutinyError> {
self.set_data(NOSTR_PROFILE_METADATA.to_string(), metadata, None)
}

Expand Down
31 changes: 31 additions & 0 deletions mutiny-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1873,6 +1873,37 @@ impl MutinyWallet {
Ok(JsValue::from_serde(&profile)?)
}

/// This should only be called when the user is setting up a new profile
/// never for an existing profile
pub async fn setup_new_profile(
&self,
name: Option<String>,
img_url: Option<String>,
lnurl: Option<String>,
nip05: Option<String>,
) -> Result<JsValue, MutinyJsError> {
let img_url = img_url
.map(|i| nostr::Url::from_str(&i))
.transpose()
.map_err(|_| MutinyJsError::InvalidArgumentsError)?;

let lnurl = lnurl
.map(|l| {
LightningAddress::from_str(&l)
.map(|a| a.lnurl())
.or(LnUrl::from_str(&l))
})
.transpose()
.map_err(|_| MutinyJsError::InvalidArgumentsError)?;

let profile = self
.inner
.nostr
.setup_new_profile(name, img_url, lnurl, nip05)
.await?;
Ok(JsValue::from_serde(&profile)?)
}

/// Sets the user's nostr profile data
#[wasm_bindgen]
pub async fn edit_nostr_profile(
Expand Down

0 comments on commit 653a196

Please sign in to comment.