diff --git a/crates/cdk-redb/src/wallet.rs b/crates/cdk-redb/src/wallet.rs index 13af6249..9e40e206 100644 --- a/crates/cdk-redb/src/wallet.rs +++ b/crates/cdk-redb/src/wallet.rs @@ -17,12 +17,15 @@ use tracing::instrument; use super::error::Error; const MINTS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mints_table"); -const MINT_KEYSETS_TABLE: MultimapTableDefinition<&str, &str> = +// +const MINT_KEYSETS_TABLE: MultimapTableDefinition<&str, &[u8]> = MultimapTableDefinition::new("mint_keysets"); +// +const KEYSETS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("keysets"); const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes"); const MELT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("melt_quotes"); const MINT_KEYS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_keys"); -// +// const PROOFS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("proofs"); const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config"); const KEYSET_COUNTER: TableDefinition<&str, u32> = TableDefinition::new("keyset_counter"); @@ -62,6 +65,7 @@ impl RedbWalletDatabase { // Open all tables to init a new db let _ = write_txn.open_table(MINTS_TABLE)?; let _ = write_txn.open_multimap_table(MINT_KEYSETS_TABLE)?; + let _ = write_txn.open_table(KEYSETS_TABLE)?; let _ = write_txn.open_table(MINT_QUOTES_TABLE)?; let _ = write_txn.open_table(MELT_QUOTES_TABLE)?; let _ = write_txn.open_table(MINT_KEYS_TABLE)?; @@ -166,9 +170,7 @@ impl WalletDatabase for RedbWalletDatabase { table .insert( mint_url.to_string().as_str(), - serde_json::to_string(&keyset) - .map_err(Error::from)? - .as_str(), + keyset.id.to_bytes().as_slice(), ) .map_err(Error::from)?; } @@ -189,14 +191,52 @@ impl WalletDatabase for RedbWalletDatabase { .open_multimap_table(MINT_KEYSETS_TABLE) .map_err(Error::from)?; - let keysets = table + let keyset_ids: Vec = table .get(mint_url.to_string().as_str()) .map_err(Error::from)? .flatten() - .flat_map(|k| serde_json::from_str(k.value())) + .flat_map(|k| Id::from_bytes(k.value())) .collect(); - Ok(keysets) + let mut keysets = vec![]; + + let keysets_t = read_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?; + + for keyset_id in keyset_ids { + if let Some(keyset) = keysets_t + .get(keyset_id.to_bytes().as_slice()) + .map_err(Error::from)? + { + let keyset = serde_json::from_str(keyset.value()).map_err(Error::from)?; + + keysets.push(keyset); + } + } + + match keysets.is_empty() { + true => Ok(None), + false => Ok(Some(keysets)), + } + } + + #[instrument(skip(self))] + async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result, Self::Err> { + let db = self.db.lock().await; + let read_txn = db.begin_read().map_err(Into::::into)?; + let table = read_txn.open_table(KEYSETS_TABLE).map_err(Error::from)?; + + match table + .get(keyset_id.to_bytes().as_slice()) + .map_err(Error::from)? + { + Some(keyset) => { + let keyset: KeySetInfo = + serde_json::from_str(keyset.value()).map_err(Error::from)?; + + Ok(Some(keyset)) + } + None => Ok(None), + } } #[instrument(skip_all)] diff --git a/crates/cdk-rexie/src/wallet.rs b/crates/cdk-rexie/src/wallet.rs index d406dc6c..de88bba7 100644 --- a/crates/cdk-rexie/src/wallet.rs +++ b/crates/cdk-rexie/src/wallet.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::rc::Rc; use std::result::Result; @@ -15,7 +15,8 @@ use tokio::sync::Mutex; // Tables const MINTS: &str = "mints"; -const MINT_KEYSETS: &str = "mint_keysets"; +const MINT_KEYSETS: &str = "keysets_by_mint"; +const KEYSETS: &str = "keysets"; const MINT_KEYS: &str = "mint_keys"; const MINT_QUOTES: &str = "mint_quotes"; const MELT_QUOTES: &str = "melt_quotes"; @@ -23,7 +24,7 @@ const PROOFS: &str = "proofs"; const CONFIG: &str = "config"; const KEYSET_COUNTER: &str = "keyset_counter"; -const DATABASE_VERSION: u32 = 1; +const DATABASE_VERSION: u32 = 2; #[derive(Debug, Error)] pub enum Error { @@ -61,15 +62,20 @@ unsafe impl Sync for RexieWalletDatabase {} impl RexieWalletDatabase { pub async fn new() -> Result { let rexie = Rexie::builder("cdk") - // Set the version of the database to 1.0 .version(DATABASE_VERSION) - // Add an object store named `employees` - .add_object_store(ObjectStore::new(PROOFS).add_index(Index::new("y", "y").unique(true))) + .add_object_store( + ObjectStore::new(PROOFS) + .add_index(Index::new("y", "y").unique(true)) + .add_index(Index::new("mint_url", "mint_url")) + .add_index(Index::new("state", "state")) + .add_index(Index::new("unit", "unit")), + ) .add_object_store( ObjectStore::new(MINTS).add_index(Index::new("mint_url", "mint_url").unique(true)), ) + .add_object_store(ObjectStore::new(MINT_KEYSETS)) .add_object_store( - ObjectStore::new(MINT_KEYSETS) + ObjectStore::new(KEYSETS) .add_index(Index::new("keyset_id", "keyset_id").unique(true)), ) .add_object_store( @@ -175,19 +181,46 @@ impl WalletDatabase for RexieWalletDatabase { let rexie = self.db.lock().await; let transaction = rexie - .transaction(&[MINT_KEYSETS], TransactionMode::ReadWrite) + .transaction(&[MINT_KEYSETS, KEYSETS], TransactionMode::ReadWrite) .map_err(Error::from)?; - let keysets_store = transaction.store(MINT_KEYSETS).map_err(Error::from)?; + let mint_keysets_store = transaction.store(MINT_KEYSETS).map_err(Error::from)?; + let keysets_store = transaction.store(KEYSETS).map_err(Error::from)?; let mint_url = serde_wasm_bindgen::to_value(&mint_url).map_err(Error::from)?; - let keysets = serde_wasm_bindgen::to_value(&keysets).map_err(Error::from)?; - keysets_store - .put(&keysets, Some(&mint_url)) + let mint_keysets = mint_keysets_store + .get(&mint_url) .await .map_err(Error::from)?; + let mut mint_keysets: Option> = + serde_wasm_bindgen::from_value(mint_keysets).map_err(Error::from)?; + + let new_keyset_ids: Vec = keysets.iter().map(|k| k.id).collect(); + + mint_keysets + .as_mut() + .unwrap_or(&mut HashSet::new()) + .extend(new_keyset_ids); + + let mint_keysets = serde_wasm_bindgen::to_value(&mint_keysets).map_err(Error::from)?; + + mint_keysets_store + .put(&mint_keysets, Some(&mint_url)) + .await + .map_err(Error::from)?; + + for keyset in keysets { + let id = serde_wasm_bindgen::to_value(&keyset.id).map_err(Error::from)?; + let keyset = serde_wasm_bindgen::to_value(&keyset).map_err(Error::from)?; + + keysets_store + .put(&keyset, Some(&id)) + .await + .map_err(Error::from)?; + } + transaction.done().await.map_err(Error::from)?; Ok(()) @@ -200,20 +233,58 @@ impl WalletDatabase for RexieWalletDatabase { let rexie = self.db.lock().await; let transaction = rexie - .transaction(&[MINT_KEYSETS], TransactionMode::ReadOnly) + .transaction(&[MINT_KEYSETS, KEYSETS], TransactionMode::ReadOnly) .map_err(Error::from)?; let mints_store = transaction.store(MINT_KEYSETS).map_err(Error::from)?; let mint_url = serde_wasm_bindgen::to_value(&mint_url).map_err(Error::from)?; - let keysets = mints_store.get(&mint_url).await.map_err(Error::from)?; + let mint_keysets = mints_store.get(&mint_url).await.map_err(Error::from)?; - let keysets: Option> = - serde_wasm_bindgen::from_value(keysets).map_err(Error::from)?; + let mint_keysets: Option> = + serde_wasm_bindgen::from_value(mint_keysets).map_err(Error::from)?; + + let keysets_store = transaction.store(KEYSETS).map_err(Error::from)?; + + let keysets = match mint_keysets { + Some(mint_keysets) => { + let mut keysets = vec![]; + + for mint_keyset in mint_keysets { + let id = serde_wasm_bindgen::to_value(&mint_keyset).map_err(Error::from)?; + + let keyset = keysets_store.get(&id).await.map_err(Error::from)?; + + let keyset = serde_wasm_bindgen::from_value(keyset).map_err(Error::from)?; + + keysets.push(keyset); + } + + Some(keysets) + } + None => None, + }; + + transaction.done().await.map_err(Error::from)?; Ok(keysets) } + async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result, Self::Err> { + let rexie = self.db.lock().await; + + let transaction = rexie + .transaction(&[KEYSETS], TransactionMode::ReadOnly) + .map_err(Error::from)?; + let keysets_store = transaction.store(KEYSETS).map_err(Error::from)?; + + let keyset_id = serde_wasm_bindgen::to_value(keyset_id).map_err(Error::from)?; + + let keyset = keysets_store.get(&keyset_id).await.map_err(Error::from)?; + + Ok(serde_wasm_bindgen::from_value(keyset).map_err(Error::from)?) + } + async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> { let rexie = self.db.lock().await; diff --git a/crates/cdk/src/cdk_database/mod.rs b/crates/cdk/src/cdk_database/mod.rs index 411bd50e..8ca4d207 100644 --- a/crates/cdk/src/cdk_database/mod.rs +++ b/crates/cdk/src/cdk_database/mod.rs @@ -64,6 +64,8 @@ pub trait WalletDatabase { &self, mint_url: UncheckedUrl, ) -> Result>, Self::Err>; + async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result, Self::Err>; + async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err>; async fn get_mint_quote(&self, quote_id: &str) -> Result, Self::Err>; async fn get_mint_quotes(&self) -> Result, Self::Err>; diff --git a/crates/cdk/src/cdk_database/wallet_memory.rs b/crates/cdk/src/cdk_database/wallet_memory.rs index 6324eff2..41d5237f 100644 --- a/crates/cdk/src/cdk_database/wallet_memory.rs +++ b/crates/cdk/src/cdk_database/wallet_memory.rs @@ -18,7 +18,8 @@ use crate::url::UncheckedUrl; #[derive(Default, Debug, Clone)] pub struct WalletMemoryDatabase { mints: Arc>>>, - mint_keysets: Arc>>>, + mint_keysets: Arc>>>, + keysets: Arc>>, mint_quotes: Arc>>, melt_quotes: Arc>>, mint_keys: Arc>>, @@ -39,6 +40,7 @@ impl WalletMemoryDatabase { Self { mints: Arc::new(RwLock::new(HashMap::new())), mint_keysets: Arc::new(RwLock::new(HashMap::new())), + keysets: Arc::new(RwLock::new(HashMap::new())), mint_quotes: Arc::new(RwLock::new( mint_quotes.into_iter().map(|q| (q.id.clone(), q)).collect(), )), @@ -83,10 +85,19 @@ impl WalletDatabase for WalletMemoryDatabase { mint_url: UncheckedUrl, keysets: Vec, ) -> Result<(), Error> { - let mut current_keysets = self.mint_keysets.write().await; + let mut current_mint_keysets = self.mint_keysets.write().await; + let mut current_keysets = self.keysets.write().await; - let mint_keysets = current_keysets.entry(mint_url).or_insert(HashSet::new()); - mint_keysets.extend(keysets); + for keyset in keysets { + current_mint_keysets + .entry(mint_url.clone()) + .and_modify(|ks| { + ks.insert(keyset.id); + }) + .or_insert(HashSet::from_iter(vec![keyset.id])); + + current_keysets.insert(keyset.id, keyset); + } Ok(()) } @@ -95,12 +106,26 @@ impl WalletDatabase for WalletMemoryDatabase { &self, mint_url: UncheckedUrl, ) -> Result>, Error> { - Ok(self - .mint_keysets - .read() - .await - .get(&mint_url) - .map(|ks| ks.iter().cloned().collect())) + match self.mint_keysets.read().await.get(&mint_url) { + Some(keyset_ids) => { + let mut keysets = vec![]; + + let db_keysets = self.keysets.read().await; + + for id in keyset_ids { + if let Some(keyset) = db_keysets.get(id) { + keysets.push(keyset.clone()); + } + } + + Ok(Some(keysets)) + } + None => Ok(None), + } + } + + async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result, Error> { + Ok(self.keysets.read().await.get(keyset_id).cloned()) } async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Error> { diff --git a/crates/cdk/src/wallet/mod.rs b/crates/cdk/src/wallet/mod.rs index a1c348bc..9b5180a0 100644 --- a/crates/cdk/src/wallet/mod.rs +++ b/crates/cdk/src/wallet/mod.rs @@ -694,14 +694,13 @@ impl Wallet { .post_swap(mint_url.clone().try_into()?, pre_swap.swap_request) .await?; + let active_keys = self.active_keys(mint_url, unit).await?.unwrap(); + let mut post_swap_proofs = construct_proofs( swap_response.signatures, pre_swap.pre_mint_secrets.rs(), pre_swap.pre_mint_secrets.secrets(), - &self - .active_keys(mint_url, unit) - .await? - .ok_or(Error::UnknownKey)?, + &active_keys, )?; let active_keyset_id = self.active_mint_keyset(mint_url, unit).await?; @@ -793,7 +792,7 @@ impl Wallet { proofs: Proofs, spending_conditions: Option, ) -> Result { - let active_keyset_id = self.active_mint_keyset(mint_url, unit).await?; + let active_keyset_id = self.active_mint_keyset(mint_url, unit).await.unwrap(); // Desired amount is either amount passwed or value of all proof let proofs_total = proofs.iter().map(|p| p.amount).sum(); @@ -1047,11 +1046,10 @@ impl Wallet { .collect(); } - let mint_keysets = self - .localstore - .get_mint_keysets(mint_url.clone()) - .await? - .ok_or(Error::UnknownKey)?; + let mint_keysets = match self.localstore.get_mint_keysets(mint_url.clone()).await? { + Some(keysets) => keysets, + None => self.get_mint_keysets(&mint_url).await?, + }; let (active, inactive): (HashSet, HashSet) = mint_keysets .into_iter()