From 33cf10627cdabc7afca5b5895cb1a3f374399da8 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Wed, 10 Apr 2024 14:51:34 -0500 Subject: [PATCH 1/3] Remove hermes dm reset date --- mutiny-core/src/hermes.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mutiny-core/src/hermes.rs b/mutiny-core/src/hermes.rs index f2c15a694..b888b5f2c 100644 --- a/mutiny-core/src/hermes.rs +++ b/mutiny-core/src/hermes.rs @@ -135,7 +135,7 @@ impl HermesClient { pub fn start(&self, profile_key: Option) -> Result<(), MutinyError> { // if we haven't synced before, use now and save to storage let last_sync_time = self.storage.get_dm_sync_time(true)?; - let mut time_stamp = match last_sync_time { + let time_stamp = match last_sync_time { None => { let now = Timestamp::from(0); self.storage.set_dm_sync_time(now.as_u64(), true)?; @@ -144,11 +144,6 @@ impl HermesClient { Some(time) => Timestamp::from(time + 1), // add one so we get only new events }; - // if we have a time stamp before the bug, reset to 0 - if time_stamp.as_u64() < 1712862315 { - time_stamp = Timestamp::from(0); - } - // check to see if we currently have an address let logger_check_clone = self.logger.clone(); let stop_check_clone = self.stop.clone(); From 31853457c4b90e7aad58731264e127b71a25d28a Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Wed, 10 Apr 2024 22:30:27 -0500 Subject: [PATCH 2/3] Change federation for hermes address --- mutiny-core/src/hermes.rs | 136 +++++++++++++++++++++++++++++++++----- mutiny-core/src/lib.rs | 29 ++++++-- 2 files changed, 144 insertions(+), 21 deletions(-) diff --git a/mutiny-core/src/hermes.rs b/mutiny-core/src/hermes.rs index b888b5f2c..13133e562 100644 --- a/mutiny-core/src/hermes.rs +++ b/mutiny-core/src/hermes.rs @@ -16,9 +16,9 @@ use futures::{pin_mut, select, FutureExt}; use lightning::util::logger::Logger; use lightning::{log_error, log_info, log_warn}; use lightning_invoice::Bolt11Invoice; -use nostr::prelude::decrypt_received_private_zap_message; use nostr::secp256k1::SecretKey; use nostr::{nips::nip04::decrypt, Event, JsonUtil, Keys, Tag, ToBech32}; +use nostr::{prelude::decrypt_received_private_zap_message, EventBuilder}; use nostr::{Filter, Kind, Timestamp}; use nostr_sdk::{Client, RelayPoolNotification}; use reqwest::Method; @@ -44,6 +44,9 @@ const HERMES_SERVICE_ID: u32 = 1; const HERMES_FREE_PLAN_ID: u32 = 1; const HERMES_PAID_PLAN_ID: u32 = 2; +const REGISTRATION_CHECK_EVENT_KIND: Kind = Kind::Custom(93_186); +const NEW_FEDERATION_EVENT_KIND: Kind = Kind::Custom(93_187); + #[derive(Deserialize, Serialize)] pub struct RegisterRequest { pub name: Option, @@ -132,7 +135,7 @@ impl HermesClient { /// Starts the hermes background checker /// 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> { + pub async fn start(&self, profile_key: Option) -> Result<(), MutinyError> { // if we haven't synced before, use now and save to storage let last_sync_time = self.storage.get_dm_sync_time(true)?; let time_stamp = match last_sync_time { @@ -148,26 +151,73 @@ impl HermesClient { 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 nostr_client_check_clone = self.client.clone(); let base_url_check_clone = self.base_url.clone(); let current_address_check_clone = self.current_address.clone(); + let first_federation = self.get_first_federation().await.clone(); utils::spawn(async move { loop { if stop_check_clone.load(Ordering::Relaxed) { break; }; - match check_hermes_name_for_pubkey( + match check_hermes_registration_info( &http_client_check_clone, &base_url_check_clone, - public_key_check_clone, + nostr_client_check_clone.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); + { + let mut c = current_address_check_clone.write().await; + log_info!( + logger_check_clone, + "checked lightning address: {:?}", + o.name + ); + *c = (o.name.clone(), true); + } + + // check that federation is still the same if the user has a name + if o.name.is_some() { + if let Some(f) = first_federation { + // if a registered federation exists and is what we have + // then there is no reason to update + if o.federation_id.is_some() + && f.federation_id == o.federation_id.unwrap() + { + break; + } + + // user has a federation registered but is different than current + // so we should update it + match change_federation_info( + &http_client_check_clone, + &base_url_check_clone, + nostr_client_check_clone, + f, + ) + .await + { + Ok(_) => { + log_info!( + logger_check_clone, + "changed federation info to current federation" + ); + } + Err(e) => { + log_error!( + logger_check_clone, + "could not change the federation: {e}" + ); + } + } + } + + // TODO handle the case where the user no longer has a federation + } + break; } Err(e) => { @@ -257,6 +307,19 @@ impl HermesClient { Ok(()) } + pub async fn change_federation_info( + &self, + federation: FederationIdentity, + ) -> Result<(), MutinyError> { + change_federation_info( + &self.http_client, + &self.base_url, + self.client.clone(), + federation, + ) + .await + } + pub async fn check_available_name(&self, name: String) -> Result { check_name_request(&self.http_client, &self.base_url, name).await } @@ -333,8 +396,38 @@ impl HermesClient { None => None, } } +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct ChangeFederationRequest { + pub name: Option, + pub federation_id: Option, +} - // TODO need a way to change the federation if the user's federation changes +async fn change_federation_info( + http_client: &reqwest::Client, + base_url: &str, + nostr_client: Client, + federation: FederationIdentity, +) -> Result<(), MutinyError> { + // create nostr event + let signer = nostr_client.signer().await?; + let event_builder = EventBuilder::new( + NEW_FEDERATION_EVENT_KIND, + federation.invite_code.to_string(), + [], + ); + let event = signer.sign_event_builder(event_builder).await?; + + // send request + let url = Url::parse(&format!("{}/v1/change-federation", base_url)) + .map_err(|_| MutinyError::ConnectionFailed)?; + let request = http_client.request(Method::POST, url).json(&event); + let _ = utils::fetch_with_timeout(http_client, request.build().expect("should build req")) + .await + .map_err(|_| MutinyError::ConnectionFailed)?; + + Ok(()) } fn find_hermes_token( @@ -347,18 +440,29 @@ fn find_hermes_token( .find(|token| token.service_id == service_id && token.plan_id == plan_id) } -async fn check_hermes_name_for_pubkey( +#[derive(Serialize, Deserialize, Clone)] +pub struct RegistrationInfo { + pub name: Option, + pub federation_id: Option, +} + +async fn check_hermes_registration_info( http_client: &reqwest::Client, base_url: &str, - pubkey: nostr::PublicKey, -) -> Result, MutinyError> { - let url = Url::parse(&format!("{}/v1/check-pubkey/{pubkey}", base_url,)) + nostr_client: Client, +) -> Result { + // create nostr event + let signer = nostr_client.signer().await?; + let event_builder = EventBuilder::new(REGISTRATION_CHECK_EVENT_KIND, "", []); + let event = signer.sign_event_builder(event_builder).await?; + + // send request + let url = Url::parse(&format!("{}/v1/check-registration", base_url)) .map_err(|_| MutinyError::ConnectionFailed)?; - let request = http_client.request(Method::GET, url); - + let request = http_client.request(Method::POST, url).json(&event); let res = utils::fetch_with_timeout(http_client, request.build().expect("should build req")) .await? - .json::>() + .json::() .await .map_err(|_| MutinyError::ConnectionFailed)?; diff --git a/mutiny-core/src/lib.rs b/mutiny-core/src/lib.rs index 803b561d3..387640305 100644 --- a/mutiny-core/src/lib.rs +++ b/mutiny-core/src/lib.rs @@ -1078,7 +1078,7 @@ impl MutinyWalletBuilder { #[cfg(target_arch = "wasm32")] NostrSigner::NIP07(_) => None, }; - mw.start_hermes(profile_key)?; + mw.start_hermes(profile_key).await?; log_info!( mw.logger, @@ -2399,6 +2399,7 @@ impl MutinyWallet { self.logger.clone(), self.federation_storage.clone(), self.federations.clone(), + self.hermes_client.clone(), federation_code, ) .await @@ -2451,6 +2452,8 @@ impl MutinyWallet { return Err(MutinyError::NotFound); } + // TODO remove the federation from hermes + Ok(()) } @@ -2856,9 +2859,9 @@ impl MutinyWallet { } /// Starts up the hermes client if available - pub fn start_hermes(&self, profile_key: Option) -> Result<(), MutinyError> { + pub async fn start_hermes(&self, profile_key: Option) -> Result<(), MutinyError> { if let Some(hermes_client) = self.hermes_client.as_ref() { - hermes_client.start(profile_key)? + hermes_client.start(profile_key).await? } Ok(()) } @@ -2969,6 +2972,7 @@ pub(crate) async fn create_new_federation( logger: Arc, federation_storage: Arc>, federations: Arc>>>>, + hermes_client: Option>>, federation_code: InviteCode, ) -> Result { // Begin with a mutex lock so that nothing else can @@ -3021,7 +3025,7 @@ pub(crate) async fn create_new_federation( .await .insert(federation_id, Arc::new(new_federation)); - Ok(FederationIdentity { + let new_federation_identity = FederationIdentity { uuid: next_federation_uuid.clone(), federation_id, invite_code: federation_code, @@ -3029,7 +3033,22 @@ pub(crate) async fn create_new_federation( federation_expiry_timestamp, welcome_message, gateway_fees, - }) + }; + + // change the federation with hermes, if available + if let Some(h) = hermes_client { + match h + .change_federation_info(new_federation_identity.clone()) + .await + { + Ok(_) => (), + Err(e) => { + log_error!(logger, "could not change hermes federation: {e}") + } + } + } + + Ok(new_federation_identity) } #[derive(Deserialize)] From ba2bf0f7abbceb7a3b89cf38fa09e7305647b74a Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Thu, 11 Apr 2024 16:45:08 -0500 Subject: [PATCH 3/3] Disable zaps when federations are removed or detected as removed --- mutiny-core/src/hermes.rs | 115 ++++++++++++++++++++++++++++++-------- mutiny-core/src/lib.rs | 10 +++- 2 files changed, 101 insertions(+), 24 deletions(-) diff --git a/mutiny-core/src/hermes.rs b/mutiny-core/src/hermes.rs index 13133e562..fc83fb863 100644 --- a/mutiny-core/src/hermes.rs +++ b/mutiny-core/src/hermes.rs @@ -46,6 +46,7 @@ const HERMES_PAID_PLAN_ID: u32 = 2; const REGISTRATION_CHECK_EVENT_KIND: Kind = Kind::Custom(93_186); const NEW_FEDERATION_EVENT_KIND: Kind = Kind::Custom(93_187); +const DISABLE_ZAPS_EVENT_KIND: Kind = Kind::Custom(93_188); #[derive(Deserialize, Serialize)] pub struct RegisterRequest { @@ -179,43 +180,58 @@ impl HermesClient { *c = (o.name.clone(), true); } - // check that federation is still the same if the user has a name - if o.name.is_some() { - if let Some(f) = first_federation { - // if a registered federation exists and is what we have - // then there is no reason to update - if o.federation_id.is_some() - && f.federation_id == o.federation_id.unwrap() - { - break; - } + // check that federation is still the same + if let Some(f) = first_federation { + // if a registered federation exists and is what we have + // then there is no reason to update + if o.federation_id.is_some() + && f.federation_id == o.federation_id.unwrap() + { + break; + } - // user has a federation registered but is different than current - // so we should update it - match change_federation_info( + // user has a federation registered but is different than current + // so we should update it + match change_federation_info( + &http_client_check_clone, + &base_url_check_clone, + nostr_client_check_clone, + current_address_check_clone, + f, + &logger_check_clone, + ) + .await + { + Ok(_) => (), + Err(e) => { + log_error!( + logger_check_clone, + "could not change the federation: {e}" + ); + } + } + } else { + // handle the case where the user no longer has a federation + // if user is already disabled, no need to call again + if !o.disabled_zaps { + match disable_zaps( &http_client_check_clone, &base_url_check_clone, nostr_client_check_clone, - f, + current_address_check_clone, + &logger_check_clone, ) .await { - Ok(_) => { - log_info!( - logger_check_clone, - "changed federation info to current federation" - ); - } + Ok(_) => (), Err(e) => { log_error!( logger_check_clone, - "could not change the federation: {e}" + "could not disable zaps: {e}" ); } } } - - // TODO handle the case where the user no longer has a federation } break; @@ -315,7 +331,20 @@ impl HermesClient { &self.http_client, &self.base_url, self.client.clone(), + self.current_address.clone(), federation, + &self.logger, + ) + .await + } + + pub async fn disable_zaps(&self) -> Result<(), MutinyError> { + disable_zaps( + &self.http_client, + &self.base_url, + self.client.clone(), + self.current_address.clone(), + &self.logger, ) .await } @@ -408,8 +437,15 @@ async fn change_federation_info( http_client: &reqwest::Client, base_url: &str, nostr_client: Client, + current_address: Arc, bool)>>, federation: FederationIdentity, + logger: &MutinyLogger, ) -> Result<(), MutinyError> { + // make sure name is registered already + if current_address.read().await.0.is_none() { + return Ok(()); + } + // create nostr event let signer = nostr_client.signer().await?; let event_builder = EventBuilder::new( @@ -427,6 +463,38 @@ async fn change_federation_info( .await .map_err(|_| MutinyError::ConnectionFailed)?; + log_info!(logger, "changed federation info to current federation"); + + Ok(()) +} + +async fn disable_zaps( + http_client: &reqwest::Client, + base_url: &str, + nostr_client: Client, + current_address: Arc, bool)>>, + logger: &MutinyLogger, +) -> Result<(), MutinyError> { + // make sure name is registered already + if current_address.read().await.0.is_none() { + return Ok(()); + } + + // create nostr event + let signer = nostr_client.signer().await?; + let event_builder = EventBuilder::new(DISABLE_ZAPS_EVENT_KIND, "", []); + let event = signer.sign_event_builder(event_builder).await?; + + // send request + let url = Url::parse(&format!("{}/v1/disable-zaps", base_url)) + .map_err(|_| MutinyError::ConnectionFailed)?; + let request = http_client.request(Method::POST, url).json(&event); + let _ = utils::fetch_with_timeout(http_client, request.build().expect("should build req")) + .await + .map_err(|_| MutinyError::ConnectionFailed)?; + + log_info!(logger, "disabled zaps for the user"); + Ok(()) } @@ -444,6 +512,7 @@ fn find_hermes_token( pub struct RegistrationInfo { pub name: Option, pub federation_id: Option, + pub disabled_zaps: bool, } async fn check_hermes_registration_info( diff --git a/mutiny-core/src/lib.rs b/mutiny-core/src/lib.rs index 387640305..6b2d72fea 100644 --- a/mutiny-core/src/lib.rs +++ b/mutiny-core/src/lib.rs @@ -2452,7 +2452,15 @@ impl MutinyWallet { return Err(MutinyError::NotFound); } - // TODO remove the federation from hermes + // remove the federation from hermes + if let Some(h) = self.hermes_client.as_ref() { + match h.disable_zaps().await { + Ok(_) => (), + Err(e) => { + log_error!(self.logger, "could not disable hermes zaps: {e}") + } + } + } Ok(()) }