diff --git a/mutiny-wasm/Cargo.toml b/mutiny-wasm/Cargo.toml index e7594f1cd..2cb60d079 100644 --- a/mutiny-wasm/Cargo.toml +++ b/mutiny-wasm/Cargo.toml @@ -41,6 +41,7 @@ bip39 = { version = "2.0.0" } getrandom = { version = "0.2", features = ["js"] } futures = "0.3.25" urlencoding = "2.1.2" +once_cell = "1.18.0" # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires diff --git a/mutiny-wasm/src/lib.rs b/mutiny-wasm/src/lib.rs index 9573945cd..31f833fd1 100644 --- a/mutiny-wasm/src/lib.rs +++ b/mutiny-wasm/src/lib.rs @@ -23,6 +23,7 @@ use bitcoin::hashes::sha256; use bitcoin::secp256k1::PublicKey; use bitcoin::util::bip32::ExtendedPrivKey; use bitcoin::{Address, Network, OutPoint, Transaction, Txid}; +use futures::lock::Mutex; use gloo_utils::format::JsValueSerdeExt; use lightning::routing::gossip::NodeId; use lightning_invoice::Bolt11Invoice; @@ -48,6 +49,15 @@ use std::{ }; use wasm_bindgen::prelude::*; +static INITIALIZED: once_cell::sync::Lazy> = + once_cell::sync::Lazy::new(|| Mutex::new(false)); + +#[cfg(test)] +async fn uninit() { + let mut init = INITIALIZED.lock().await; + *init = false; +} + #[wasm_bindgen] pub struct MutinyWallet { mnemonic: Mnemonic, @@ -84,6 +94,13 @@ impl MutinyWallet { skip_device_lock: Option, safe_mode: Option, ) -> Result { + let mut init = INITIALIZED.lock().await; + if *init { + return Err(MutinyJsError::AlreadyRunning); + } else { + *init = true; + } + utils::set_panic_hook(); let safe_mode = safe_mode.unwrap_or(false); let logger = Arc::new(MutinyLogger::default()); @@ -1461,8 +1478,9 @@ impl MutinyWallet { #[cfg(test)] mod tests { use crate::utils::test::*; - use crate::MutinyWallet; + use crate::{uninit, MutinyWallet}; + use crate::error::MutinyJsError; use crate::indexed_db::IndexedDbStorage; use js_sys::Array; use mutiny_core::storage::MutinyStorage; @@ -1502,6 +1520,65 @@ mod tests { IndexedDbStorage::clear() .await .expect("failed to clear storage"); + uninit().await; + } + + #[test] + async fn fail_to_create_2_mutiny_wallets() { + log!("trying to create 2 mutiny wallets!"); + let password = Some("password".to_string()); + + assert!(!MutinyWallet::has_node_manager(password.clone()).await); + MutinyWallet::new( + password.clone(), + None, + None, + Some("regtest".to_owned()), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ) + .await + .expect("mutiny wallet should initialize"); + super::utils::sleep(1_000).await; + assert!(MutinyWallet::has_node_manager(password.clone()).await); + + // try to create a second + let result = MutinyWallet::new( + password.clone(), + None, + None, + Some("regtest".to_owned()), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + ) + .await; + + if let Err(MutinyJsError::AlreadyRunning) = result { + // this is the expected error + } else { + panic!("should have failed to create a second mutiny wallet"); + }; + + IndexedDbStorage::clear() + .await + .expect("failed to clear storage"); + uninit().await; } #[test] @@ -1539,6 +1616,7 @@ mod tests { IndexedDbStorage::clear() .await .expect("failed to clear storage"); + uninit().await; } #[test] @@ -1583,6 +1661,7 @@ mod tests { IndexedDbStorage::clear() .await .expect("failed to clear storage"); + uninit().await; } fn js_to_option_vec_string(js_val: JsValue) -> Result>, JsValue> { @@ -1657,6 +1736,7 @@ mod tests { IndexedDbStorage::clear() .await .expect("failed to clear storage"); + uninit().await; } #[test] @@ -1712,5 +1792,6 @@ mod tests { IndexedDbStorage::clear() .await .expect("failed to clear storage"); + uninit().await; } }