Skip to content

Commit

Permalink
Idb Dependendecy injection
Browse files Browse the repository at this point in the history
  • Loading branch information
grod220 committed Jul 5, 2024
1 parent 1e1d02b commit 56b5b56
Show file tree
Hide file tree
Showing 17 changed files with 674 additions and 328 deletions.
1 change: 1 addition & 0 deletions packages/wasm/crate/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/wasm/crate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ ark-ff = { version = "0.4.2", features = ["std"] }
base64 = "0.22.1"
console_error_panic_hook = { version = "0.1.7", optional = true }
decaf377 = { version = "0.5.0", features = ["r1cs"] }
futures = "0.3.30"
hex = "0.4.3"
indexed_db_futures = "0.4.1"
prost = "0.12.6"
Expand Down
130 changes: 130 additions & 0 deletions packages/wasm/crate/src/database/indexed_db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use std::future::IntoFuture;

use indexed_db_futures::idb_object_store::IdbObjectStoreParameters;
use indexed_db_futures::prelude::{IdbOpenDbRequestLike, OpenDbRequest};
use indexed_db_futures::{IdbDatabase, IdbKeyPath, IdbQuerySource, IdbVersionChangeEvent};
use serde::de::DeserializeOwned;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::IdbTransactionMode::Readwrite;

use crate::database::interface::Database;
use crate::error::WasmResult;
use crate::storage::DbConstants;

pub async fn open_idb_database(constants: &DbConstants) -> WasmResult<IdbDatabase> {
#[allow(unused_mut)]
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&constants.name, constants.version)?;

// Conditionally mock sample `IdbDatabase` database for testing purposes
#[cfg(feature = "mock-database")]
let db_req = mock_test_database(db_req).into_future().await;

let db = db_req.into_future().await?;
Ok(db)
}

// TODO: Code comment for context
async fn mock_test_database(mut db_req: OpenDbRequest) -> OpenDbRequest {
db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
// Check if the object store exists; create it if it doesn't
if evt.db().name() == "penumbra-db-wasm-test" {
let note_key: JsValue = serde_wasm_bindgen::to_value("noteCommitment.inner")?;
let note_object_store_params = IdbObjectStoreParameters::new()
.key_path(Some(&IdbKeyPath::new(note_key)))
.to_owned();
let note_object_store = evt
.db()
.create_object_store_with_params("SPENDABLE_NOTES", &note_object_store_params)?;

let nullifier_key: JsValue = serde_wasm_bindgen::to_value("nullifier.inner")?;
note_object_store.create_index_with_params(
"nullifier",
&IdbKeyPath::new(nullifier_key),
web_sys::IdbIndexParameters::new().unique(false),
)?;
evt.db().create_object_store("TREE_LAST_POSITION")?;
evt.db().create_object_store("TREE_LAST_FORGOTTEN")?;

let commitment_key: JsValue = serde_wasm_bindgen::to_value("commitment.inner")?;
let commitment_object_store_params = IdbObjectStoreParameters::new()
.key_path(Some(&IdbKeyPath::new(commitment_key)))
.to_owned();
evt.db().create_object_store_with_params(
"TREE_COMMITMENTS",
&commitment_object_store_params,
)?;
evt.db().create_object_store("TREE_HASHES")?;
evt.db().create_object_store("FMD_PARAMETERS")?;
evt.db().create_object_store("APP_PARAMETERS")?;
evt.db().create_object_store("GAS_PRICES")?;
}
Ok(())
}));

db_req
}

impl Database for IdbDatabase {
async fn get<T, K>(&self, table: &str, key: K, index: Option<&str>) -> WasmResult<Option<T>>
where
T: DeserializeOwned,
K: Into<JsValue>,
{
let tx = self.transaction_on_one(table)?;
let store = tx.object_store(table)?;

let js_value = match index {
Some(i) => store.index(i)?.get_owned(key)?.await?,
None => store.get_owned(key)?.await?,
};

let result = js_value.map(serde_wasm_bindgen::from_value).transpose()?;

Ok(result)
}

async fn get_latest<T>(&self, table: &str) -> WasmResult<Option<T>>
where
T: DeserializeOwned,
{
let tx = self.transaction_on_one(table)?;
let store = tx.object_store(table)?;

Ok(store
.open_cursor_with_direction(web_sys::IdbCursorDirection::Prev)?
.await?
.and_then(|cursor| serde_wasm_bindgen::from_value(cursor.value()).ok()))
}

async fn get_all<T: DeserializeOwned>(&self, table: &str) -> WasmResult<Vec<T>> {
let idb_tx = self.transaction_on_one(table)?;
let store = idb_tx.object_store(table)?;
let results = store.get_all()?.await?;
let serialized = results
.into_iter()
.map(serde_wasm_bindgen::from_value)
.collect::<Result<Vec<T>, _>>()?;
Ok(serialized)
}

async fn put<V>(&self, table: &str, value: V) -> WasmResult<()>
where
V: Into<JsValue>,
{
let tx = self.transaction_on_one_with_mode(table, Readwrite)?;
let store = tx.object_store(table)?;
store.put_val_owned(value)?;
Ok(())
}

async fn put_with_key<K, V>(&self, table: &str, key: K, value: &V) -> WasmResult<()>
where
K: Into<JsValue>,
V: JsCast,
{
let tx = self.transaction_on_one_with_mode(table, Readwrite)?;
let store = tx.object_store(table)?;
store.put_key_val_owned(key, value)?;
Ok(())
}
}
38 changes: 38 additions & 0 deletions packages/wasm/crate/src/database/interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::future::Future;

use serde::de::DeserializeOwned;
use wasm_bindgen::{JsCast, JsValue};

use crate::error::WasmResult;

pub trait Database {
fn get<T, K>(
&self,
table: &str,
key: K,
index: Option<&str>,
) -> impl Future<Output = WasmResult<Option<T>>>
where
T: DeserializeOwned,
K: Into<JsValue>;

// Gets the most recent record in table
fn get_latest<T>(&self, table: &str) -> impl Future<Output = WasmResult<Option<T>>>
where
T: DeserializeOwned;
fn get_all<T: DeserializeOwned>(&self, table: &str)
-> impl Future<Output = WasmResult<Vec<T>>>;
fn put<V>(&self, table: &str, value: V) -> impl Future<Output = WasmResult<()>>
where
V: Into<JsValue>;

fn put_with_key<K, V>(
&self,
table: &str,
key: K,
value: &V,
) -> impl Future<Output = WasmResult<()>>
where
K: Into<JsValue>,
V: JsCast;
}
114 changes: 114 additions & 0 deletions packages/wasm/crate/src/database/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use serde::de::DeserializeOwned;
use wasm_bindgen::{JsCast, JsValue};

use crate::database::interface::Database;
use crate::error::WasmResult;
use crate::storage::Tables;

pub fn get_mock_tables() -> Tables {
Tables {
assets: "assets".to_string(),
advice_notes: "advice_notes".to_string(),
spendable_notes: "spendable_notes".to_string(),
swaps: "swaps".to_string(),
fmd_parameters: "fmd_parameters".to_string(),
app_parameters: "app_parameters".to_string(),
gas_prices: "gas_prices".to_string(),
epochs: "epochs".to_string(),
transactions: "transactions".to_string(),
full_sync_height: "full_sync_height".to_string(),
auctions: "auctions".to_string(),
auction_outstanding_reserves: "auction_outstanding_reserves".to_string(),
}
}

type DbTable = Rc<RefCell<HashMap<String, JsValue>>>;

pub struct MockDb {
tables: RefCell<HashMap<String, DbTable>>,
}

impl Default for MockDb {
fn default() -> Self {
Self::new()
}
}

impl MockDb {
pub fn new() -> Self {
MockDb {
tables: RefCell::new(Default::default()),
}
}

fn get_table(&self, table: &str) -> DbTable {
let mut tables = self.tables.borrow_mut();
tables
.entry(table.to_string())
.or_insert_with(|| Rc::new(RefCell::new(HashMap::new())))
.clone()
}
}

impl Database for MockDb {
async fn get<T, K>(&self, table: &str, key: K, _index: Option<&str>) -> WasmResult<Option<T>>
where
T: DeserializeOwned,
K: Into<JsValue>,
{
let table = self.get_table(table);
let key = key.into().as_string().unwrap_or_default();

let result = table
.borrow()
.get(&key)
.and_then(|js_value| serde_wasm_bindgen::from_value(js_value.clone()).ok());

Ok(result)
}

async fn get_latest<T>(&self, _table: &str) -> WasmResult<Option<T>>
where
T: DeserializeOwned,
{
unimplemented!()
}

async fn get_all<T: DeserializeOwned>(&self, table: &str) -> WasmResult<Vec<T>> {
let table = self.get_table(table);
let table_ref = table.borrow();

let mut results = Vec::new();
for js_value in table_ref.values() {
if let Ok(item) = serde_wasm_bindgen::from_value(js_value.clone()) {
results.push(item);
}
}

Ok(results)
}

async fn put<V>(&self, _table: &str, _value: V) -> WasmResult<()>
where
V: Into<JsValue>,
{
unimplemented!()
}

async fn put_with_key<K, V>(&self, table: &str, key: K, value: &V) -> WasmResult<()>
where
K: Into<JsValue>,
V: JsCast,
{
let table = self.get_table(table);
let key = key.into().as_string().unwrap_or_default();

table.borrow_mut().insert(key, value.into());

Ok(())
}
}
3 changes: 3 additions & 0 deletions packages/wasm/crate/src/database/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod indexed_db;
pub mod interface;
pub mod mock;
1 change: 1 addition & 0 deletions packages/wasm/crate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extern crate core;
pub mod asset;
pub mod auction;
pub mod build;
pub mod database;
pub mod dex;
pub mod error;
pub mod keys;
Expand Down
Loading

0 comments on commit 56b5b56

Please sign in to comment.