diff --git a/Cargo.lock b/Cargo.lock index bd2aee7..8b214bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "accessory" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87537f9ae7cfa78d5b8ebd1a1db25959f5e737126be4d8eb44a5452fc4b63cde" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "aead" version = "0.5.2" @@ -320,6 +332,18 @@ dependencies = [ "typenum", ] +[[package]] +name = "delegate-display" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -379,6 +403,18 @@ dependencies = [ "blake2b_simd", ] +[[package]] +name = "fancy_constructor" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "fastrand" version = "2.1.0" @@ -548,6 +584,23 @@ dependencies = [ "either", ] +[[package]] +name = "indexed_db_futures" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43315957678a70eb21fb0d2384fe86dde0d6c859a01e24ce127eb65a0143d28c" +dependencies = [ + "accessory", + "cfg-if", + "delegate-display", + "fancy_constructor", + "js-sys", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "indexmap" version = "2.3.0" @@ -638,6 +691,53 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "macroific" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd" +dependencies = [ + "macroific_attr_parse", + "macroific_core", + "macroific_macro", +] + +[[package]] +name = "macroific_attr_parse" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "macroific_core" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "macroific_macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae" +dependencies = [ + "macroific_attr_parse", + "macroific_core", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -1291,6 +1391,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", + "wasm-bindgen", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1410,12 +1520,14 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "getrandom", + "indexed_db_futures", "js-sys", "thiserror", "tonic-build", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", + "web-sys", "which", "zcash_keys", "zcash_primitives", diff --git a/Cargo.toml b/Cargo.toml index 355efb6..6cc75bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ getrandom = { version = "0.2", features = ["js"] } thiserror = "1.0.63" console_error_panic_hook = { version = "0.1.7", optional = true } wasm-bindgen-futures = "0.4.42" +indexed_db_futures = "0.5.0" +web-sys = { version = "0.3.69", features = ["console"] } [dev-dependencies] wasm-bindgen-test = "0.3.42" diff --git a/src/error.rs b/src/error.rs index 9853fe3..399a742 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,12 @@ pub enum Error { // DecodingError(#[from] zcash_keys::keys::DecodingError), #[error("Javascript error")] JsError(JsValue), + #[error("DomException {name} ({code}): {message}")] + DomException { + name: String, + message: String, + code: u16, + }, } impl From for JsValue { @@ -26,3 +32,13 @@ impl From for Error { Error::JsError(e) } } + +impl From for Error { + fn from(e: indexed_db_futures::web_sys::DomException) -> Self { + Self::DomException { + name: e.name(), + message: e.message(), + code: e.code(), + } + } +} diff --git a/src/store/indexed_db_store.rs b/src/store/indexed_db_store.rs new file mode 100644 index 0000000..567d6df --- /dev/null +++ b/src/store/indexed_db_store.rs @@ -0,0 +1,73 @@ +// Copyright 2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::error::Error; +use crate::store::WalletStore; +use indexed_db_futures::prelude::*; +use js_sys::Uint8Array; +use wasm_bindgen::JsValue; + +static DB_NAME: &str = "test_db"; +static KV_STORE: &str = "kv_store"; +/// A simple IndexedDb store for wallet data +pub struct IdbStore { + pub inner: indexed_db_futures::IdbDatabase, +} + +impl WalletStore for IdbStore { + async fn update(&mut self, key: &str, value: &[u8]) -> Result<(), Error> { + let tx = self + .inner + .transaction_on_one_with_mode(KV_STORE, IdbTransactionMode::Readwrite)?; + let store = tx.object_store(KV_STORE)?; + + store.put_key_val_owned(JsValue::from_str(key), &Uint8Array::from(value))?; + tx.await.into_result()?; + Ok(()) + } + + async fn get(&self, key: &str) -> Result>, Error> { + let tx = self + .inner + .transaction_on_one_with_mode(KV_STORE, IdbTransactionMode::Readonly)?; + let store = tx.object_store(KV_STORE)?; + match store.get(&JsValue::from_str(key))?.await? { + Some(v) => { + let v = Uint8Array::from(v); + Ok(Some(v.to_vec())) + } + None => Ok(None), + } + } + + async fn clear(&mut self, key: &str) -> Result<(), Error> { + let tx = self + .inner + .transaction_on_one_with_mode(KV_STORE, IdbTransactionMode::Readwrite)?; + let store = tx.object_store(KV_STORE)?; + store.delete_owned(&JsValue::from_str(key))?; + tx.await.into_result()?; + Ok(()) + } +} + +impl IdbStore { + pub async fn new() -> Result { + let mut db_req = IdbDatabase::open_u32(DB_NAME, 1)?; + // let db = open.await?; + db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> { + let create_store_if_needed = + |evt: &IdbVersionChangeEvent, store_key: &'static str| -> Result<(), JsValue> { + if let None = evt.db().object_store_names().find(|n| n == store_key) { + evt.db().create_object_store(store_key)?; + } + Ok(()) + }; + create_store_if_needed(evt, &KV_STORE)?; + Ok(()) + })); + Ok(Self { + inner: db_req.await?, + }) + } +} diff --git a/src/store/injected_store.rs b/src/store/injected_store.rs index fdfb642..c3b0f47 100644 --- a/src/store/injected_store.rs +++ b/src/store/injected_store.rs @@ -1,8 +1,8 @@ // Copyright 2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use crate::store::WalletStore; use crate::error::Error; +use crate::store::WalletStore; use wasm_bindgen::prelude::*; #[wasm_bindgen(typescript_custom_section)] @@ -50,9 +50,12 @@ impl WalletStore for InjectedStore { Ok(()) } - async fn get(&self, key: &str) -> Result, Error> { + async fn get(&self, key: &str) -> Result>, Error> { let result = InjectedStore::get(self, key).await?; - Ok(js_sys::Uint8Array::new(&result).to_vec()) + if result.is_null() { + return Ok(None); + } + Ok(Some(js_sys::Uint8Array::new(&result).to_vec())) } async fn clear(&mut self, key: &str) -> Result<(), Error> { diff --git a/src/store/memory_store.rs b/src/store/memory_store.rs index 42ce153..748ff4f 100644 --- a/src/store/memory_store.rs +++ b/src/store/memory_store.rs @@ -1,8 +1,8 @@ // Copyright 2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use crate::store::WalletStore; use crate::error::Error; +use crate::store::WalletStore; /// A simple in-memory store for wallet data. Useful for testing pub struct MemoryStore { @@ -15,8 +15,8 @@ impl WalletStore for MemoryStore { Ok(()) } - async fn get(&self, key: &str) -> Result, Error> { - Ok(self.inner.get(key).unwrap().to_vec()) + async fn get(&self, key: &str) -> Result>, Error> { + Ok(self.inner.get(key).cloned()) } async fn clear(&mut self, key: &str) -> Result<(), Error> { diff --git a/src/store/mod.rs b/src/store/mod.rs index a0f9fba..aa88268 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -1,7 +1,9 @@ +mod indexed_db_store; mod injected_store; mod memory_store; mod wallet_store; +pub use indexed_db_store::IdbStore; pub use injected_store::InjectedStore; pub use memory_store::MemoryStore; pub use wallet_store::WalletStore; diff --git a/src/store/wallet_store.rs b/src/store/wallet_store.rs index 76c5e78..7a27ac9 100644 --- a/src/store/wallet_store.rs +++ b/src/store/wallet_store.rs @@ -5,6 +5,6 @@ use crate::error::Error; pub trait WalletStore { async fn update(&mut self, key: &str, value: &[u8]) -> Result<(), Error>; - async fn get(&self, key: &str) -> Result, Error>; + async fn get(&self, key: &str) -> Result>, Error>; async fn clear(&mut self, key: &str) -> Result<(), Error>; } diff --git a/src/wallet.rs b/src/wallet.rs index 998a935..4b7d828 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -20,7 +20,7 @@ impl Wallet { pub fn add(&self, _account: Account) {} pub async fn get(&self, _index: AccountIndex) -> Result { - Account::from_bytes(&self.store.get("yer").await?) + Account::from_bytes(&self.store.get("yer").await?.unwrap()) } } diff --git a/tests/indexed_db_store.rs b/tests/indexed_db_store.rs new file mode 100644 index 0000000..cc0d3df --- /dev/null +++ b/tests/indexed_db_store.rs @@ -0,0 +1,29 @@ +use wasm_bindgen_test::*; +use web_sys::console; +wasm_bindgen_test_configure!(run_in_browser); +use webz_core::store::{self, WalletStore as _}; +#[wasm_bindgen_test] +fn pass() { + assert_eq!(1, 1); +} + +#[wasm_bindgen_test] +async fn idb() { + let k = "key"; + let v = vec![1, 2, 3]; + let v2 = vec![1, 2, 3, 4]; + // Add to store + let mut store = store::IdbStore::new().await.unwrap(); + store.update(k, &v).await.unwrap(); + + // Get from store + assert_eq!(store.get(k).await.unwrap().unwrap(), v); + + // Update Key + store.update(k, &v2).await.unwrap(); + assert_eq!(store.get(k).await.unwrap().unwrap(), v2); + + // Clear Key + store.clear(k).await.unwrap(); + assert_eq!(store.get(k).await.unwrap(), None); +}