diff --git a/mutiny-core/src/hermes.rs b/mutiny-core/src/hermes.rs index 4d5731806..550250e68 100644 --- a/mutiny-core/src/hermes.rs +++ b/mutiny-core/src/hermes.rs @@ -64,6 +64,8 @@ pub struct HermesClient { pub(crate) federations: Arc>>>>, blind_auth: Arc>, base_url: String, + // bool represents whether or not it was successfully checked + current_address: Arc, bool)>>, storage: S, pub logger: Arc, pub stop: Arc, @@ -100,8 +102,6 @@ impl HermesClient { .await .expect("Failed to add relays"); - // TODO need to store the fact that we have a LNURL or not... - Ok(Self { dm_key: keys, public_key, @@ -110,6 +110,7 @@ impl HermesClient { base_url, federations, blind_auth, + current_address: Arc::new(RwLock::new((None, false))), storage: storage.clone(), logger, stop, @@ -120,25 +121,59 @@ impl HermesClient { /// This should only error if there's an initial unrecoverable error /// Otherwise it will loop in the background until a stop signal pub fn start(&self, profile_key: Option) -> Result<(), MutinyError> { - let logger = self.logger.clone(); - let stop = self.stop.clone(); - let client = self.client.clone(); - let public_key = self.public_key; - let storage = self.storage.clone(); - let dm_key = self.dm_key.clone(); - let federations = self.federations.clone(); - // if we haven't synced before, use now and save to storage - let last_sync_time = storage.get_dm_sync_time(true)?; + let last_sync_time = self.storage.get_dm_sync_time(true)?; let time_stamp = match last_sync_time { None => { let now = Timestamp::now(); - storage.set_dm_sync_time(now.as_u64(), true)?; + self.storage.set_dm_sync_time(now.as_u64(), true)?; now } Some(time) => Timestamp::from(time + 1), // add one so we get only new events }; + // check to see if we currently have an address + let logger_check_clone = self.logger.clone(); + let stop_check_clone = self.stop.clone(); + let http_client_check_clone = self.http_client.clone(); + let public_key_check_clone = self.public_key; + let base_url_check_clone = self.base_url.clone(); + let current_address_check_clone = self.current_address.clone(); + utils::spawn(async move { + loop { + if stop_check_clone.load(Ordering::Relaxed) { + break; + }; + + match check_hermes_name_for_pubkey( + &http_client_check_clone, + &base_url_check_clone, + public_key_check_clone, + ) + .await + { + Ok(o) => { + let mut c = current_address_check_clone.write().await; + log_info!(logger_check_clone, "checked lightning address: {o:?}"); + *c = (o, true); + break; + } + Err(e) => { + log_error!(logger_check_clone, "error checking lightning address: {e}"); + } + }; + + utils::sleep(1_000).await; + } + }); + + let logger = self.logger.clone(); + let stop = self.stop.clone(); + let client = self.client.clone(); + let public_key = self.public_key; + let storage = self.storage.clone(); + let dm_key = self.dm_key.clone(); + let federations = self.federations.clone(); utils::spawn(async move { loop { if stop.load(Ordering::Relaxed) { @@ -243,14 +278,23 @@ impl HermesClient { // send the register request let req = RegisterRequest { - name: Some(name), + name: Some(name.clone()), pubkey: self.public_key.to_string(), federation_invite_code: federation_identity.invite_code.to_string(), msg: nonce.to_message(), sig: unblinded_sig, }; register_name(&self.http_client.clone(), &self.base_url, req).await?; - // TODO store the fact that this succeeded + + { + let mut c = self.current_address.write().await; + log_info!( + self.logger, + "registered lightning address: {}", + name.clone() + ); + *c = (Some(name), true); + } // Mark the token as spent successfully self.blind_auth.used_token(available_paid_token).await?; @@ -258,6 +302,17 @@ impl HermesClient { Ok(()) } + pub async fn check_username(&self) -> Result, MutinyError> { + match self.current_address.read().await.clone() { + (None, false) => Err(MutinyError::ConnectionFailed), + (Some(n), true) => Ok(Some(n)), + (None, true) => Ok(None), + _ => { + unreachable!("can't have some and false") + } + } + } + async fn get_first_federation(&self) -> Option { let federations = self.federations.read().await; match federations.iter().next() { @@ -279,6 +334,24 @@ fn find_hermes_token( .find(|token| token.service_id == service_id && token.plan_id == plan_id) } +async fn check_hermes_name_for_pubkey( + http_client: &reqwest::Client, + base_url: &str, + pubkey: nostr::PublicKey, +) -> Result, MutinyError> { + let url = Url::parse(&format!("{}/v1/check-pubkey/{pubkey}", base_url,)) + .map_err(|_| MutinyError::ConnectionFailed)?; + let request = http_client.request(Method::GET, url); + + let res = utils::fetch_with_timeout(http_client, request.build().expect("should build req")) + .await? + .json::>() + .await + .map_err(|_| MutinyError::ConnectionFailed)?; + + Ok(res) +} + async fn check_name_request( http_client: &reqwest::Client, base_url: &str, diff --git a/mutiny-core/src/lib.rs b/mutiny-core/src/lib.rs index 7e47f4282..df752d614 100644 --- a/mutiny-core/src/lib.rs +++ b/mutiny-core/src/lib.rs @@ -2774,6 +2774,14 @@ impl MutinyWallet { } } + pub async fn check_lnurl_name(&self) -> Result, MutinyError> { + if let Some(hermes_client) = self.hermes_client.as_ref() { + hermes_client.check_username().await + } else { + Err(MutinyError::NotFound) + } + } + /// Starts up the hermes client if available pub fn start_hermes(&self, profile_key: Option) -> Result<(), MutinyError> { if let Some(hermes_client) = self.hermes_client.as_ref() { diff --git a/mutiny-wasm/src/lib.rs b/mutiny-wasm/src/lib.rs index 9dc8fa28f..dc8347227 100644 --- a/mutiny-wasm/src/lib.rs +++ b/mutiny-wasm/src/lib.rs @@ -1962,6 +1962,11 @@ impl MutinyWallet { Ok(self.inner.reserve_lnurl_name(name).await?) } + /// Checks the registered username for the user + pub async fn check_lnurl_name(&self) -> Result, MutinyJsError> { + Ok(self.inner.check_lnurl_name().await?) + } + /// Resets the scorer and network graph. This can be useful if you get stuck in a bad state. #[wasm_bindgen] pub async fn reset_router(&self) -> Result<(), MutinyJsError> {