Skip to content

Commit

Permalink
Merge pull request #784 from MutinyWallet/detect-subscribed
Browse files Browse the repository at this point in the history
Detect subscribed in backend
  • Loading branch information
TonyGiorgio authored Nov 8, 2023
2 parents 46b0194 + 3e4feac commit 0214f3f
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 47 deletions.
24 changes: 16 additions & 8 deletions mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub mod redshift;
pub mod scb;
pub mod scorer;
pub mod storage;
mod subscription;
pub mod subscription;
pub mod vss;

#[cfg(any(test, feature = "test-utils"))]
Expand All @@ -55,6 +55,7 @@ use crate::nostr::nwc::{
BudgetPeriod, BudgetedSpendingConditions, NwcProfileTag, SpendingConditions,
};
use crate::storage::{MutinyStorage, DEVICE_ID_KEY, EXPECTED_NETWORK_KEY, NEED_FULL_SYNC_KEY};
use crate::subscription::MutinySubscriptionClient;
use crate::{error::MutinyError, nostr::ReservedProfile};
use crate::{nodemanager::NodeManager, nostr::ProfileType};
use crate::{nostr::NostrManager, utils::sleep};
Expand Down Expand Up @@ -84,7 +85,7 @@ pub struct MutinyWalletConfig {
user_rgs_url: Option<String>,
lsp_url: Option<String>,
auth_client: Option<Arc<MutinyAuthClient>>,
subscription_url: Option<String>,
subscription_client: Option<Arc<MutinySubscriptionClient>>,
scorer_url: Option<String>,
do_not_connect_peers: bool,
skip_device_lock: bool,
Expand All @@ -101,7 +102,7 @@ impl MutinyWalletConfig {
user_rgs_url: Option<String>,
lsp_url: Option<String>,
auth_client: Option<Arc<MutinyAuthClient>>,
subscription_url: Option<String>,
subscription_client: Option<Arc<MutinySubscriptionClient>>,
scorer_url: Option<String>,
skip_device_lock: bool,
) -> Self {
Expand All @@ -115,7 +116,7 @@ impl MutinyWalletConfig {
scorer_url,
lsp_url,
auth_client,
subscription_url,
subscription_client,
do_not_connect_peers: false,
skip_device_lock,
safe_mode: false,
Expand Down Expand Up @@ -179,6 +180,13 @@ impl<S: MutinyStorage> MutinyWallet<S> {
nostr,
};

// if we have a subscription, ensure we have the mutiny subscription profile
if mw.storage.premium() {
if let Some(ref sub) = mw.node_manager.subscription_client {
mw.ensure_mutiny_nwc_profile(sub, false).await?;
}
}

#[cfg(not(test))]
{
// if we need a full sync from a restore
Expand Down Expand Up @@ -362,7 +370,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
// account for 3 day grace period
if expired_time + 86_400 * 3 > crate::utils::now().as_secs() {
// now submit the NWC string if never created before
self.ensure_mutiny_nwc_profile(subscription_client, false)
self.ensure_mutiny_nwc_profile(subscription_client.as_ref(), false)
.await?;
}
}
Expand Down Expand Up @@ -398,7 +406,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
.await?;

// now submit the NWC string if never created before
self.ensure_mutiny_nwc_profile(subscription_client, autopay)
self.ensure_mutiny_nwc_profile(subscription_client.as_ref(), autopay)
.await?;

Ok(())
Expand All @@ -407,9 +415,9 @@ impl<S: MutinyStorage> MutinyWallet<S> {
}
}

async fn ensure_mutiny_nwc_profile(
pub(crate) async fn ensure_mutiny_nwc_profile(
&self,
subscription_client: Arc<subscription::MutinySubscriptionClient>,
subscription_client: &MutinySubscriptionClient,
autopay: bool,
) -> Result<(), MutinyError> {
let nwc_profiles = self.nostr.profiles();
Expand Down
19 changes: 4 additions & 15 deletions mutiny-core/src/nodemanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,21 +765,10 @@ impl<S: MutinyStorage> NodeManager<S> {
.expect("failed to make lnurl client"),
);

let (subscription_client, auth) = if let Some(auth_client) = c.auth_client {
if let Some(subscription_url) = c.subscription_url {
let auth = auth_client.auth.clone();
let s = Arc::new(MutinySubscriptionClient::new(
auth_client,
subscription_url,
logger.clone(),
));
(Some(s), auth)
} else {
(None, auth_client.auth.clone())
}
let auth = if let Some(auth) = c.auth_client {
auth.auth.clone()
} else {
let auth_manager = AuthManager::new(c.xprivkey)?;
(None, auth_manager)
AuthManager::new(c.xprivkey)?
};

let price_cache = storage
Expand Down Expand Up @@ -807,7 +796,7 @@ impl<S: MutinyStorage> NodeManager<S> {
auth,
lnurl_client,
lsp_clients,
subscription_client,
subscription_client: c.subscription_client,
logger,
bitcoin_price_cache: Arc::new(Mutex::new(price_cache)),
do_not_connect_peers: c.do_not_connect_peers,
Expand Down
5 changes: 5 additions & 0 deletions mutiny-core/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ pub trait MutinyStorage: Clone + Sized + 'static {
/// Get the VSS client used for storage
fn vss_client(&self) -> Option<Arc<MutinyVssClient>>;

/// If the VSS client has premium features enabledss
fn premium(&self) -> bool {
self.vss_client().is_some_and(|v| v.premium)
}

/// Set a value in the storage, the value will already be encrypted if needed
fn set<T>(&self, key: impl AsRef<str>, value: T) -> Result<(), MutinyError>
where
Expand Down
14 changes: 8 additions & 6 deletions mutiny-core/src/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,27 @@ use serde::{Deserialize, Serialize};

use crate::{auth::MutinyAuthClient, error::MutinyError, logging::MutinyLogger, nodemanager::Plan};

pub(crate) struct MutinySubscriptionClient {
pub struct MutinySubscriptionClient {
auth_client: Arc<MutinyAuthClient>,
url: String,
logger: Arc<MutinyLogger>,
}

impl MutinySubscriptionClient {
pub(crate) fn new(
auth_client: Arc<MutinyAuthClient>,
url: String,
logger: Arc<MutinyLogger>,
) -> Self {
pub fn new(auth_client: Arc<MutinyAuthClient>, url: String, logger: Arc<MutinyLogger>) -> Self {
Self {
auth_client,
url,
logger,
}
}

/// Checks whether or not the user is subscribed to Mutiny+.
/// Submits a NWC string to keep the subscription active if not expired.
///
/// Returns None if there's no subscription at all.
/// Returns Some(u64) for their unix expiration timestamp, which may be in the
/// past or in the future, depending on whether or not it is currently active.
pub async fn check_subscribed(&self) -> Result<Option<u64>, MutinyError> {
let url = Url::parse(&format!("{}/v1/check-subscribed", self.url)).map_err(|e| {
log_error!(self.logger, "Error parsing check subscribed url: {e}");
Expand Down
3 changes: 3 additions & 0 deletions mutiny-core/src/vss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct MutinyVssClient {
url: String,
store_id: Option<String>,
encryption_key: SecretKey,
pub premium: bool,
pub logger: Arc<MutinyLogger>,
}

Expand Down Expand Up @@ -88,6 +89,7 @@ impl MutinyVssClient {
url,
store_id: None, // we get this from the auth client
encryption_key,
premium: false, // set later
logger,
}
}
Expand All @@ -105,6 +107,7 @@ impl MutinyVssClient {
url,
store_id: Some(pk),
encryption_key,
premium: true, // unauthenticated clients are self-hosted so they are premium
logger,
}
}
Expand Down
59 changes: 41 additions & 18 deletions mutiny-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ use bitcoin::util::bip32::ExtendedPrivKey;
use bitcoin::{Address, Network, OutPoint, Transaction, Txid};
use futures::lock::Mutex;
use gloo_utils::format::JsValueSerdeExt;
use lightning::log_info;
use lightning::routing::gossip::NodeId;
use lightning::util::logger::Logger;
use lightning_invoice::Bolt11Invoice;
use lnurl::lnurl::LnUrl;
use mutiny_core::auth::MutinyAuthClient;
Expand All @@ -35,7 +37,8 @@ use mutiny_core::redshift::RedshiftManager;
use mutiny_core::redshift::RedshiftRecipient;
use mutiny_core::scb::EncryptedSCB;
use mutiny_core::storage::MutinyStorage;
use mutiny_core::utils::sleep;
use mutiny_core::subscription::MutinySubscriptionClient;
use mutiny_core::utils::{now, sleep};
use mutiny_core::vss::MutinyVssClient;
use mutiny_core::{labels::LabelStorage, nodemanager::NodeManager};
use mutiny_core::{logging::MutinyLogger, nostr::ProfileType};
Expand Down Expand Up @@ -168,7 +171,7 @@ impl MutinyWallet {
let seed = mnemonic.to_seed("");
let xprivkey = ExtendedPrivKey::new_master(network, &seed).unwrap();

let (auth_client, vss_client) = if safe_mode {
let (auth_client, mut vss_client) = if safe_mode {
(None, None)
} else if let Some(auth_url) = auth_url.clone() {
let auth_manager = AuthManager::new(xprivkey).unwrap();
Expand All @@ -187,28 +190,53 @@ impl MutinyWallet {
));

let vss = storage_url.map(|url| {
Arc::new(MutinyVssClient::new_authenticated(
MutinyVssClient::new_authenticated(
auth_client.clone(),
url,
xprivkey.private_key,
logger.clone(),
))
)
});

(Some(auth_client), vss)
} else {
let vss = storage_url.map(|url| {
Arc::new(MutinyVssClient::new_unauthenticated(
url,
xprivkey.private_key,
logger.clone(),
))
MutinyVssClient::new_unauthenticated(url, xprivkey.private_key, logger.clone())
});

(None, vss)
};

let storage = IndexedDbStorage::new(password, cipher, vss_client, logger.clone()).await?;
let (subscription_client, subscribed) = if let Some(ref auth_client) = auth_client {
if let Some(subscription_url) = subscription_url {
let client = Arc::new(MutinySubscriptionClient::new(
auth_client.clone(),
subscription_url,
logger.clone(),
));

// check if we're subscribed, add 3 day grace period
let sub_expired_time = client.check_subscribed().await?.unwrap_or_default();
let subscribed = sub_expired_time + 86_400 * 3 > now().as_secs();

(Some(client), subscribed)
} else {
(None, false)
}
} else {
(None, false)
};

if subscribed {
log_info!(logger, "Welcome back Mutiny+ user!");
if let Some(vss) = vss_client.as_mut() {
vss.premium = true;
}
}

let storage =
IndexedDbStorage::new(password, cipher, vss_client.map(Arc::new), logger.clone())
.await?;

let mut config = mutiny_core::MutinyWalletConfig::new(
xprivkey,
Expand All @@ -218,7 +246,7 @@ impl MutinyWallet {
user_rgs_url,
lsp_url,
auth_client,
subscription_url,
subscription_client,
scorer_url,
skip_device_lock.unwrap_or(false),
);
Expand Down Expand Up @@ -1382,14 +1410,9 @@ impl MutinyWallet {
}

/// Checks whether or not the user is subscribed to Mutiny+.
/// Submits a NWC string to keep the subscription active if not expired.
///
/// Returns None if there's no subscription at all.
/// Returns Some(u64) for their unix expiration timestamp, which may be in the
/// past or in the future, depending on whether or not it is currently active.
#[wasm_bindgen]
pub async fn check_subscribed(&self) -> Result<Option<u64>, MutinyJsError> {
Ok(self.inner.check_subscribed().await?)
pub fn check_subscribed(&self) -> bool {
self.inner.storage.premium()
}

/// Gets the subscription plans for Mutiny+ subscriptions
Expand Down

0 comments on commit 0214f3f

Please sign in to comment.