Skip to content
This repository was archived by the owner on Feb 3, 2025. It is now read-only.

Commit 60be71e

Browse files
Merge pull request #868 from MutinyWallet/device-lock-improvements
Device Lock Improvements
2 parents ca921a9 + 9db01b8 commit 60be71e

4 files changed

Lines changed: 87 additions & 9 deletions

File tree

mutiny-core/src/nodemanager.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use std::{collections::HashMap, ops::Deref, sync::Arc};
6060
use uuid::Uuid;
6161

6262
const BITCOIN_PRICE_CACHE_SEC: u64 = 300;
63-
pub const DEVICE_LOCK_INTERVAL_SECS: u64 = 60;
63+
pub const DEVICE_LOCK_INTERVAL_SECS: u64 = 30;
6464

6565
// This is the NodeStorage object saved to the DB
6666
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -549,7 +549,7 @@ impl<S: MutinyStorage> NodeManager<S> {
549549
if let Some(lock) = storage.get_device_lock()? {
550550
log_info!(logger, "Current device lock: {lock:?}");
551551
}
552-
storage.set_device_lock()?;
552+
storage.set_device_lock().await?;
553553
}
554554

555555
let storage_clone = storage.clone();
@@ -561,7 +561,7 @@ impl<S: MutinyStorage> NodeManager<S> {
561561
break;
562562
}
563563
sleep((DEVICE_LOCK_INTERVAL_SECS * 1_000) as i32).await;
564-
if let Err(e) = storage_clone.set_device_lock() {
564+
if let Err(e) = storage_clone.set_device_lock().await {
565565
log_error!(logger_clone, "Error setting device lock: {e}");
566566
}
567567
}

mutiny-core/src/storage.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ pub struct DeviceLock {
8282
}
8383

8484
impl DeviceLock {
85+
pub fn remaining_secs(&self) -> u64 {
86+
let now = now().as_secs();
87+
let diff = now.saturating_sub(self.time as u64);
88+
(DEVICE_LOCK_INTERVAL_SECS * 2).saturating_sub(diff)
89+
}
90+
8591
/// Check if the device is locked
8692
/// This is determined if the time is less than 2 minutes ago
8793
pub fn is_locked(&self, id: &str) -> bool {
@@ -385,7 +391,7 @@ pub trait MutinyStorage: Clone + Sized + 'static {
385391
self.get_data(DEVICE_LOCK_KEY)
386392
}
387393

388-
fn set_device_lock(&self) -> Result<(), MutinyError> {
394+
async fn set_device_lock(&self) -> Result<(), MutinyError> {
389395
let device = self.get_device_id()?;
390396
if let Some(lock) = self.get_device_lock()? {
391397
if lock.is_locked(&device) {
@@ -395,7 +401,7 @@ pub trait MutinyStorage: Clone + Sized + 'static {
395401

396402
let time = now().as_secs() as u32;
397403
let lock = DeviceLock { time, device };
398-
self.set_data(DEVICE_LOCK_KEY, lock, Some(time))
404+
self.set_data_async(DEVICE_LOCK_KEY, lock, Some(time)).await
399405
}
400406

401407
async fn fetch_device_lock(&self) -> Result<Option<DeviceLock>, MutinyError>;
@@ -714,7 +720,7 @@ mod tests {
714720
let lock = storage.get_device_lock().unwrap();
715721
assert_eq!(None, lock);
716722

717-
storage.set_device_lock().unwrap();
723+
storage.set_device_lock().await.unwrap();
718724
// sleep 1 second to make sure it writes to VSS
719725
sleep(1_000).await;
720726

@@ -725,7 +731,7 @@ mod tests {
725731
assert_eq!(lock.unwrap().device, id);
726732

727733
// make sure we can set lock again, should work because same device id
728-
storage.set_device_lock().unwrap();
734+
storage.set_device_lock().await.unwrap();
729735
// sleep 1 second to make sure it writes to VSS
730736
sleep(1_000).await;
731737

@@ -744,6 +750,9 @@ mod tests {
744750
assert!(lock.clone().unwrap().is_locked(&new_id));
745751
assert_eq!(lock.unwrap().device, id);
746752

747-
assert!(storage.set_device_lock().is_err())
753+
assert_eq!(
754+
storage.set_device_lock().await,
755+
Err(crate::MutinyError::AlreadyRunning)
756+
);
748757
}
749758
}

mutiny-wasm/src/indexed_db.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ impl IndexedDbStorage {
133133
key: &str,
134134
data: &Value,
135135
) -> Result<(), MutinyError> {
136+
// Device lock is only saved to VSS
137+
if key == DEVICE_LOCK_KEY {
138+
return Ok(());
139+
}
140+
136141
let tx = indexed_db
137142
.try_write()
138143
.map_err(|e| MutinyError::read_err(e.into()))

mutiny-wasm/src/lib.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use mutiny_core::lnurlauth::AuthManager;
3333
use mutiny_core::nostr::nwc::{BudgetedSpendingConditions, NwcProfileTag, SpendingConditions};
3434
use mutiny_core::redshift::RedshiftManager;
3535
use mutiny_core::redshift::RedshiftRecipient;
36-
use mutiny_core::storage::MutinyStorage;
36+
use mutiny_core::storage::{DeviceLock, MutinyStorage, DEVICE_LOCK_KEY};
3737
use mutiny_core::utils::sleep;
3838
use mutiny_core::vss::MutinyVssClient;
3939
use mutiny_core::{labels::LabelStorage, nodemanager::NodeManager};
@@ -264,6 +264,70 @@ impl MutinyWallet {
264264
NodeManager::has_node_manager(storage)
265265
}
266266

267+
/// Returns the number of remaining seconds until the device lock expires.
268+
#[wasm_bindgen]
269+
pub async fn get_device_lock_remaining_secs(
270+
password: Option<String>,
271+
auth_url: Option<String>,
272+
storage_url: Option<String>,
273+
) -> Result<Option<u64>, MutinyJsError> {
274+
let logger = Arc::new(MutinyLogger::default());
275+
let cipher = password
276+
.as_ref()
277+
.filter(|p| !p.is_empty())
278+
.map(|p| encryption_key_from_pass(p))
279+
.transpose()?;
280+
let mnemonic =
281+
IndexedDbStorage::get_mnemonic(None, password.as_deref(), cipher.clone()).await?;
282+
283+
let seed = mnemonic.to_seed("");
284+
// Network doesn't matter here, only for encoding
285+
let xprivkey = ExtendedPrivKey::new_master(Network::Bitcoin, &seed).unwrap();
286+
287+
let vss_client = if let Some(auth_url) = auth_url {
288+
let auth_manager = AuthManager::new(xprivkey).unwrap();
289+
290+
let lnurl_client = Arc::new(
291+
lnurl::Builder::default()
292+
.build_async()
293+
.expect("failed to make lnurl client"),
294+
);
295+
296+
let auth_client = Arc::new(MutinyAuthClient::new(
297+
auth_manager,
298+
lnurl_client,
299+
logger.clone(),
300+
auth_url,
301+
));
302+
303+
storage_url.map(|url| {
304+
Arc::new(MutinyVssClient::new_authenticated(
305+
auth_client.clone(),
306+
url,
307+
xprivkey.private_key,
308+
logger.clone(),
309+
))
310+
})
311+
} else {
312+
storage_url.map(|url| {
313+
Arc::new(MutinyVssClient::new_unauthenticated(
314+
url,
315+
xprivkey.private_key,
316+
logger.clone(),
317+
))
318+
})
319+
};
320+
321+
if let Some(vss) = vss_client {
322+
let obj = vss.get_object(DEVICE_LOCK_KEY).await?;
323+
let lock = serde_json::from_value::<DeviceLock>(obj.value)?;
324+
325+
return Ok(Some(lock.remaining_secs()));
326+
};
327+
328+
Ok(None)
329+
}
330+
267331
/// Starts up all the nodes again.
268332
/// Not needed after [NodeManager]'s `new()` function.
269333
#[wasm_bindgen]

0 commit comments

Comments
 (0)