diff --git a/crates/cdk-axum/Cargo.toml b/crates/cdk-axum/Cargo.toml index d1a6a3d0..861a028c 100644 --- a/crates/cdk-axum/Cargo.toml +++ b/crates/cdk-axum/Cargo.toml @@ -29,6 +29,7 @@ moka = { version = "0.11.1", features = ["future"] } serde_json = "1" paste = "1.0.15" serde = { version = "1.0.210", features = ["derive"] } +uuid = { version = "1", features = ["v4", "serde"] } [features] swagger = ["cdk/swagger", "dep:utoipa"] diff --git a/crates/cdk-axum/src/lib.rs b/crates/cdk-axum/src/lib.rs index 6c0c2423..1acc9370 100644 --- a/crates/cdk-axum/src/lib.rs +++ b/crates/cdk-axum/src/lib.rs @@ -45,6 +45,8 @@ mod swagger_imports { #[cfg(feature = "swagger")] use swagger_imports::*; +#[cfg(feature = "swagger")] +use uuid::Uuid; /// CDK Mint State #[derive(Clone)] @@ -75,16 +77,16 @@ pub struct MintState { KeySet, KeySetInfo, KeySetVersion, - MeltBolt11Request, + MeltBolt11Request, MeltQuoteBolt11Request, - MeltQuoteBolt11Response, + MeltQuoteBolt11Response, MeltQuoteState, MeltMethodSettings, - MintBolt11Request, + MintBolt11Request, MintBolt11Response, MintInfo, MintQuoteBolt11Request, - MintQuoteBolt11Response, + MintQuoteBolt11Response, MintQuoteState, MintMethodSettings, MintVersion, diff --git a/crates/cdk-axum/src/router_handlers.rs b/crates/cdk-axum/src/router_handlers.rs index 4d6e7e01..0d2bfac4 100644 --- a/crates/cdk-axum/src/router_handlers.rs +++ b/crates/cdk-axum/src/router_handlers.rs @@ -13,6 +13,7 @@ use cdk::nuts::{ use cdk::util::unix_time; use cdk::Error; use paste::paste; +use uuid::Uuid; use crate::ws::main_websocket; use crate::MintState; @@ -50,8 +51,16 @@ macro_rules! post_cache_wrapper { } post_cache_wrapper!(post_swap, SwapRequest, SwapResponse); -post_cache_wrapper!(post_mint_bolt11, MintBolt11Request, MintBolt11Response); -post_cache_wrapper!(post_melt_bolt11, MeltBolt11Request, MeltQuoteBolt11Response); +post_cache_wrapper!( + post_mint_bolt11, + MintBolt11Request, + MintBolt11Response +); +post_cache_wrapper!( + post_melt_bolt11, + MeltBolt11Request, + MeltQuoteBolt11Response +); #[cfg_attr(feature = "swagger", utoipa::path( get, @@ -137,7 +146,7 @@ pub async fn get_keysets(State(state): State) -> Result, Json(payload): Json, -) -> Result, Response> { +) -> Result>, Response> { let quote = state .mint .get_mint_bolt11_quote(payload) @@ -164,8 +173,8 @@ pub async fn post_mint_bolt11_quote( /// Get mint quote state. pub async fn get_check_mint_bolt11_quote( State(state): State, - Path(quote_id): Path, -) -> Result, Response> { + Path(quote_id): Path, +) -> Result>, Response> { let quote = state .mint .check_mint_quote("e_id) @@ -199,7 +208,7 @@ pub async fn ws_handler(State(state): State, ws: WebSocketUpgrade) -> ))] pub async fn post_mint_bolt11( State(state): State, - Json(payload): Json, + Json(payload): Json>, ) -> Result, Response> { let res = state .mint @@ -227,7 +236,7 @@ pub async fn post_mint_bolt11( pub async fn post_melt_bolt11_quote( State(state): State, Json(payload): Json, -) -> Result, Response> { +) -> Result>, Response> { let quote = state .mint .get_melt_bolt11_quote(&payload) @@ -254,8 +263,8 @@ pub async fn post_melt_bolt11_quote( /// Get melt quote state. pub async fn get_check_melt_bolt11_quote( State(state): State, - Path(quote_id): Path, -) -> Result, Response> { + Path(quote_id): Path, +) -> Result>, Response> { let quote = state .mint .check_melt_quote("e_id) @@ -283,8 +292,8 @@ pub async fn get_check_melt_bolt11_quote( /// Requests tokens to be destroyed and sent out via Lightning. pub async fn post_melt_bolt11( State(state): State, - Json(payload): Json, -) -> Result, Response> { + Json(payload): Json>, +) -> Result>, Response> { let res = state .mint .melt_bolt11(&payload) diff --git a/crates/cdk-fake-wallet/Cargo.toml b/crates/cdk-fake-wallet/Cargo.toml index 650a4604..7412f693 100644 --- a/crates/cdk-fake-wallet/Cargo.toml +++ b/crates/cdk-fake-wallet/Cargo.toml @@ -20,7 +20,6 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", thiserror = "1" serde = "1" serde_json = "1" -uuid = { version = "1", features = ["v4"] } lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } tokio-stream = "0.1.15" rand = "0.8.5" diff --git a/crates/cdk-integration-tests/tests/regtest.rs b/crates/cdk-integration-tests/tests/regtest.rs index 107fa49e..c88a86fb 100644 --- a/crates/cdk-integration-tests/tests/regtest.rs +++ b/crates/cdk-integration-tests/tests/regtest.rs @@ -120,7 +120,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> { _ => panic!("Wrong payload"), }; assert_eq!(payload.amount + payload.fee_reserve, 100.into()); - assert_eq!(payload.quote, melt.id); + assert_eq!(payload.quote.to_string(), melt.id); assert_eq!(payload.state, MeltQuoteState::Unpaid); // get current state @@ -131,7 +131,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> { _ => panic!("Wrong payload"), }; assert_eq!(payload.amount + payload.fee_reserve, 100.into()); - assert_eq!(payload.quote, melt.id); + assert_eq!(payload.quote.to_string(), melt.id); assert_eq!(payload.state, MeltQuoteState::Paid); Ok(()) diff --git a/crates/cdk-redb/Cargo.toml b/crates/cdk-redb/Cargo.toml index 68599c41..a3d78ae4 100644 --- a/crates/cdk-redb/Cargo.toml +++ b/crates/cdk-redb/Cargo.toml @@ -24,3 +24,4 @@ tracing = { version = "0.1", default-features = false, features = ["attributes", serde = { version = "1", default-features = false, features = ["derive"] } serde_json = "1" lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } +uuid = { version = "1", features = ["v4", "serde"] } diff --git a/crates/cdk-redb/src/mint/migrations.rs b/crates/cdk-redb/src/mint/migrations.rs index 90feaeff..40e5681e 100644 --- a/crates/cdk-redb/src/mint/migrations.rs +++ b/crates/cdk-redb/src/mint/migrations.rs @@ -8,12 +8,15 @@ use cdk::mint_url::MintUrl; use cdk::nuts::{CurrencyUnit, MintQuoteState, Proof, State}; use cdk::Amount; use lightning_invoice::Bolt11Invoice; -use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition}; +use redb::{ + Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition, +}; use serde::{Deserialize, Serialize}; +use uuid::Uuid; use super::{Error, PROOFS_STATE_TABLE, PROOFS_TABLE, QUOTE_SIGNATURES_TABLE}; -const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes"); +const ID_STR_MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes"); const PENDING_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("pending_proofs"); const SPENT_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("spent_proofs"); @@ -36,10 +39,158 @@ pub fn migrate_03_to_04(db: Arc) -> Result { Ok(4) } +pub fn migrate_04_to_05(db: Arc) -> Result { + let write_txn = db.begin_write()?; + + // Mint quotes + { + const MINT_QUOTE_TABLE_NAME: &str = "mint_quotes"; + const OLD_TABLE: TableDefinition<&str, &str> = TableDefinition::new(MINT_QUOTE_TABLE_NAME); + const NEW_TABLE: TableDefinition<[u8; 16], &str> = + TableDefinition::new(MINT_QUOTE_TABLE_NAME); + + let old_table = write_txn.open_table(OLD_TABLE)?; + + let mut tmp_hashmap = HashMap::new(); + + for (k, v) in old_table.iter().map_err(Error::from)?.flatten() { + let quote_id = Uuid::try_parse(k.value()).unwrap(); + tmp_hashmap.insert(quote_id, v.value().to_string()); + } + + write_txn.delete_table(old_table).map_err(Error::from)?; + let mut new_table = write_txn.open_table(NEW_TABLE)?; + + for (k, v) in tmp_hashmap.into_iter() { + new_table + .insert(k.as_bytes(), v.as_str()) + .map_err(Error::from)?; + } + } + // Melt quotes + { + const MELT_QUOTE_TABLE_NAME: &str = "melt_quotes"; + const OLD_TABLE: TableDefinition<&str, &str> = TableDefinition::new(MELT_QUOTE_TABLE_NAME); + const NEW_TABLE: TableDefinition<[u8; 16], &str> = + TableDefinition::new(MELT_QUOTE_TABLE_NAME); + + let old_table = write_txn.open_table(OLD_TABLE)?; + + let mut tmp_hashmap = HashMap::new(); + + for (k, v) in old_table.iter().map_err(Error::from)?.flatten() { + let quote_id = Uuid::try_parse(k.value()).unwrap(); + tmp_hashmap.insert(quote_id, v.value().to_string()); + } + + write_txn.delete_table(old_table).map_err(Error::from)?; + let mut new_table = write_txn.open_table(NEW_TABLE)?; + + for (k, v) in tmp_hashmap.into_iter() { + new_table + .insert(k.as_bytes(), v.as_str()) + .map_err(Error::from)?; + } + } + // Quote proofs + { + const QUOTE_PROOFS_TABLE_NAME: &str = "quote_proofs"; + const OLD_TABLE: MultimapTableDefinition<&str, [u8; 33]> = + MultimapTableDefinition::new(QUOTE_PROOFS_TABLE_NAME); + const NEW_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> = + MultimapTableDefinition::new(QUOTE_PROOFS_TABLE_NAME); + + let old_table = write_txn.open_multimap_table(OLD_TABLE)?; + + let mut tmp_hashmap = HashMap::new(); + + for (k, v) in old_table.iter().map_err(Error::from)?.flatten() { + let quote_id = Uuid::try_parse(k.value()).unwrap(); + let ys: Vec<[u8; 33]> = v.into_iter().flatten().map(|v| v.value()).collect(); + tmp_hashmap.insert(quote_id, ys); + } + + write_txn + .delete_multimap_table(old_table) + .map_err(Error::from)?; + let mut new_table = write_txn.open_multimap_table(NEW_TABLE)?; + + for (quote_id, blind_messages) in tmp_hashmap.into_iter() { + for blind_message in blind_messages { + new_table + .insert(quote_id.as_bytes(), blind_message) + .map_err(Error::from)?; + } + } + } + // Quote signatures + { + const QUOTE_SIGNATURES_TABLE_NAME: &str = "quote_signatures"; + const OLD_TABLE: MultimapTableDefinition<&str, [u8; 33]> = + MultimapTableDefinition::new(QUOTE_SIGNATURES_TABLE_NAME); + const NEW_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> = + MultimapTableDefinition::new(QUOTE_SIGNATURES_TABLE_NAME); + + let old_table = write_txn.open_multimap_table(OLD_TABLE)?; + + let mut tmp_hashmap = HashMap::new(); + + for (k, v) in old_table.iter().map_err(Error::from)?.flatten() { + let quote_id = Uuid::try_parse(k.value()).unwrap(); + let ys: Vec<[u8; 33]> = v.into_iter().flatten().map(|v| v.value()).collect(); + tmp_hashmap.insert(quote_id, ys); + } + + write_txn + .delete_multimap_table(old_table) + .map_err(Error::from)?; + let mut new_table = write_txn.open_multimap_table(NEW_TABLE)?; + + for (quote_id, signatures) in tmp_hashmap.into_iter() { + for signature in signatures { + new_table + .insert(quote_id.as_bytes(), signature) + .map_err(Error::from)?; + } + } + } + // Melt requests + { + const MELT_REQUESTS_TABLE_NAME: &str = "melt_requests"; + const OLD_TABLE: TableDefinition<&str, (&str, &str)> = + TableDefinition::new(MELT_REQUESTS_TABLE_NAME); + const NEW_TABLE: TableDefinition<[u8; 16], (&str, &str)> = + TableDefinition::new(MELT_REQUESTS_TABLE_NAME); + + let old_table = write_txn.open_table(OLD_TABLE)?; + + let mut tmp_hashmap = HashMap::new(); + + for (k, v) in old_table.iter().map_err(Error::from)?.flatten() { + let quote_id = Uuid::try_parse(k.value()).unwrap(); + let value = v.value(); + tmp_hashmap.insert(quote_id, (value.0.to_string(), value.1.to_string())); + } + + write_txn.delete_table(old_table).map_err(Error::from)?; + let mut new_table = write_txn.open_table(NEW_TABLE)?; + + for (k, v) in tmp_hashmap.into_iter() { + new_table + .insert(k.as_bytes(), (v.0.as_str(), v.1.as_str())) + .map_err(Error::from)?; + } + } + + write_txn.commit().map_err(Error::from)?; + + Ok(5) +} + /// Mint Quote Info #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] struct V1MintQuote { - pub id: String, + pub id: Uuid, pub mint_url: MintUrl, pub amount: Amount, pub unit: CurrencyUnit, @@ -66,7 +217,7 @@ impl From for MintQuote { fn migrate_mint_quotes_01_to_02(db: Arc) -> Result<(), Error> { let read_txn = db.begin_read().map_err(Error::from)?; let table = read_txn - .open_table(MINT_QUOTES_TABLE) + .open_table(ID_STR_MINT_QUOTES_TABLE) .map_err(Error::from)?; let mint_quotes: HashMap>; @@ -94,7 +245,7 @@ fn migrate_mint_quotes_01_to_02(db: Arc) -> Result<(), Error> { { let mut table = write_txn - .open_table(MINT_QUOTES_TABLE) + .open_table(ID_STR_MINT_QUOTES_TABLE) .map_err(Error::from)?; for (quote_id, quote) in migrated_mint_quotes { match quote { diff --git a/crates/cdk-redb/src/mint/mod.rs b/crates/cdk-redb/src/mint/mod.rs index 0ca822e9..c5517c86 100644 --- a/crates/cdk-redb/src/mint/mod.rs +++ b/crates/cdk-redb/src/mint/mod.rs @@ -17,8 +17,9 @@ use cdk::nuts::{ }; use cdk::types::LnKey; use cdk::{cdk_database, mint}; -use migrations::migrate_01_to_02; +use migrations::{migrate_01_to_02, migrate_04_to_05}; use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition}; +use uuid::Uuid; use super::error::Error; use crate::migrations::migrate_00_to_01; @@ -28,22 +29,23 @@ mod migrations; const ACTIVE_KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("active_keysets"); const KEYSETS_TABLE: TableDefinition<&str, &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_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("mint_quotes"); +const MELT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("melt_quotes"); const PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs"); const PROOFS_STATE_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs_state"); const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config"); // Key is hex blinded_message B_ value is blinded_signature const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> = TableDefinition::new("blinded_signatures"); -const QUOTE_PROOFS_TABLE: MultimapTableDefinition<&str, [u8; 33]> = +const QUOTE_PROOFS_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> = MultimapTableDefinition::new("quote_proofs"); -const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<&str, [u8; 33]> = +const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> = MultimapTableDefinition::new("quote_signatures"); -const MELT_REQUESTS: TableDefinition<&str, (&str, &str)> = TableDefinition::new("melt_requests"); +const MELT_REQUESTS: TableDefinition<[u8; 16], (&str, &str)> = + TableDefinition::new("melt_requests"); -const DATABASE_VERSION: u32 = 4; +const DATABASE_VERSION: u32 = 5; /// Mint Redbdatabase #[derive(Debug, Clone)] @@ -93,6 +95,10 @@ impl MintRedbDatabase { current_file_version = migrate_03_to_04(Arc::clone(&db))?; } + if current_file_version == 4 { + current_file_version = migrate_04_to_05(Arc::clone(&db))?; + } + if current_file_version != DATABASE_VERSION { tracing::warn!( "Database upgrade did not complete at {} current is {}", @@ -261,7 +267,7 @@ impl MintDatabase for MintRedbDatabase { .map_err(Error::from)?; table .insert( - quote.id.as_str(), + quote.id.as_bytes(), serde_json::to_string("e).map_err(Error::from)?.as_str(), ) .map_err(Error::from)?; @@ -271,13 +277,13 @@ impl MintDatabase for MintRedbDatabase { Ok(()) } - async fn get_mint_quote(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_mint_quote(&self, quote_id: &Uuid) -> Result, Self::Err> { let read_txn = self.db.begin_read().map_err(Error::from)?; let table = read_txn .open_table(MINT_QUOTES_TABLE) .map_err(Error::from)?; - match table.get(quote_id).map_err(Error::from)? { + match table.get(quote_id.as_bytes()).map_err(Error::from)? { Some(quote) => Ok(serde_json::from_str(quote.value()).map_err(Error::from)?), None => Ok(None), } @@ -285,7 +291,7 @@ impl MintDatabase for MintRedbDatabase { async fn update_mint_quote_state( &self, - quote_id: &str, + quote_id: &Uuid, state: MintQuoteState, ) -> Result { let write_txn = self.db.begin_write().map_err(Error::from)?; @@ -297,7 +303,7 @@ impl MintDatabase for MintRedbDatabase { .map_err(Error::from)?; let quote_guard = table - .get(quote_id) + .get(quote_id.as_bytes()) .map_err(Error::from)? .ok_or(Error::UnknownMintInfo)?; @@ -316,7 +322,7 @@ impl MintDatabase for MintRedbDatabase { table .insert( - quote_id, + quote_id.as_bytes(), serde_json::to_string(&mint_quote) .map_err(Error::from)? .as_str(), @@ -376,14 +382,14 @@ impl MintDatabase for MintRedbDatabase { Ok(quotes) } - async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> { + async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> { let write_txn = self.db.begin_write().map_err(Error::from)?; { let mut table = write_txn .open_table(MINT_QUOTES_TABLE) .map_err(Error::from)?; - table.remove(quote_id).map_err(Error::from)?; + table.remove(quote_id.as_bytes()).map_err(Error::from)?; } write_txn.commit().map_err(Error::from)?; @@ -399,7 +405,7 @@ impl MintDatabase for MintRedbDatabase { .map_err(Error::from)?; table .insert( - quote.id.as_str(), + quote.id.as_bytes(), serde_json::to_string("e).map_err(Error::from)?.as_str(), ) .map_err(Error::from)?; @@ -409,20 +415,20 @@ impl MintDatabase for MintRedbDatabase { Ok(()) } - async fn get_melt_quote(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_melt_quote(&self, quote_id: &Uuid) -> Result, Self::Err> { let read_txn = self.db.begin_read().map_err(Error::from)?; let table = read_txn .open_table(MELT_QUOTES_TABLE) .map_err(Error::from)?; - let quote = table.get(quote_id).map_err(Error::from)?; + let quote = table.get(quote_id.as_bytes()).map_err(Error::from)?; Ok(quote.map(|q| serde_json::from_str(q.value()).unwrap())) } async fn update_melt_quote_state( &self, - quote_id: &str, + quote_id: &Uuid, state: MeltQuoteState, ) -> Result { let write_txn = self.db.begin_write().map_err(Error::from)?; @@ -435,7 +441,7 @@ impl MintDatabase for MintRedbDatabase { .map_err(Error::from)?; let quote_guard = table - .get(quote_id) + .get(quote_id.as_bytes()) .map_err(Error::from)? .ok_or(Error::UnknownMintInfo)?; @@ -454,7 +460,7 @@ impl MintDatabase for MintRedbDatabase { table .insert( - quote_id, + quote_id.as_bytes(), serde_json::to_string(&melt_quote) .map_err(Error::from)? .as_str(), @@ -483,21 +489,21 @@ impl MintDatabase for MintRedbDatabase { Ok(quotes) } - async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> { + async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> { let write_txn = self.db.begin_write().map_err(Error::from)?; { let mut table = write_txn .open_table(MELT_QUOTES_TABLE) .map_err(Error::from)?; - table.remove(quote_id).map_err(Error::from)?; + table.remove(quote_id.as_bytes()).map_err(Error::from)?; } write_txn.commit().map_err(Error::from)?; Ok(()) } - async fn add_proofs(&self, proofs: Proofs, quote_id: Option) -> Result<(), Self::Err> { + async fn add_proofs(&self, proofs: Proofs, quote_id: Option) -> Result<(), Self::Err> { let write_txn = self.db.begin_write().map_err(Error::from)?; { @@ -519,7 +525,7 @@ impl MintDatabase for MintRedbDatabase { if let Some(quote_id) = "e_id { quote_proofs_table - .insert(quote_id.as_str(), y) + .insert(quote_id.as_bytes(), y) .map_err(Error::from)?; } } @@ -547,13 +553,13 @@ impl MintDatabase for MintRedbDatabase { Ok(proofs) } - async fn get_proof_ys_by_quote_id(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result, Self::Err> { let read_txn = self.db.begin_read().map_err(Error::from)?; let table = read_txn .open_multimap_table(QUOTE_PROOFS_TABLE) .map_err(Error::from)?; - let ys = table.get(quote_id).map_err(Error::from)?; + let ys = table.get(quote_id.as_bytes()).map_err(Error::from)?; let proof_ys = ys.fold(Vec::new(), |mut acc, y| { if let Ok(y) = y { @@ -660,7 +666,7 @@ impl MintDatabase for MintRedbDatabase { &self, blinded_messages: &[PublicKey], blind_signatures: &[BlindSignature], - quote_id: Option, + quote_id: Option, ) -> Result<(), Self::Err> { let write_txn = self.db.begin_write().map_err(Error::from)?; @@ -681,7 +687,7 @@ impl MintDatabase for MintRedbDatabase { if let Some(quote_id) = "e_id { quote_sigs_table - .insert(quote_id.as_str(), blinded_message.to_bytes()) + .insert(quote_id.as_bytes(), blinded_message.to_bytes()) .map_err(Error::from)?; } } @@ -740,7 +746,7 @@ impl MintDatabase for MintRedbDatabase { /// Add melt request async fn add_melt_request( &self, - melt_request: MeltBolt11Request, + melt_request: MeltBolt11Request, ln_key: LnKey, ) -> Result<(), Self::Err> { let write_txn = self.db.begin_write().map_err(Error::from)?; @@ -748,7 +754,7 @@ impl MintDatabase for MintRedbDatabase { table .insert( - melt_request.quote.as_str(), + melt_request.quote.as_bytes(), ( serde_json::to_string(&melt_request)?.as_str(), serde_json::to_string(&ln_key)?.as_str(), @@ -761,12 +767,12 @@ impl MintDatabase for MintRedbDatabase { /// Get melt request async fn get_melt_request( &self, - quote_id: &str, - ) -> Result, Self::Err> { + quote_id: &Uuid, + ) -> Result, LnKey)>, Self::Err> { let read_txn = self.db.begin_read().map_err(Error::from)?; let table = read_txn.open_table(MELT_REQUESTS).map_err(Error::from)?; - match table.get(quote_id).map_err(Error::from)? { + match table.get(quote_id.as_bytes()).map_err(Error::from)? { Some(melt_request) => { let (melt_request_str, ln_key_str) = melt_request.value(); let melt_request = serde_json::from_str(melt_request_str)?; @@ -781,14 +787,14 @@ impl MintDatabase for MintRedbDatabase { /// Get [`BlindSignature`]s for quote async fn get_blind_signatures_for_quote( &self, - quote_id: &str, + quote_id: &Uuid, ) -> Result, Self::Err> { let read_txn = self.db.begin_read().map_err(Error::from)?; let quote_proofs_table = read_txn .open_multimap_table(QUOTE_SIGNATURES_TABLE) .map_err(Error::from)?; - let ys = quote_proofs_table.get(quote_id).unwrap(); + let ys = quote_proofs_table.get(quote_id.as_bytes()).unwrap(); let ys: Vec<[u8; 33]> = ys.into_iter().flatten().map(|v| v.value()).collect(); diff --git a/crates/cdk-sqlite/Cargo.toml b/crates/cdk-sqlite/Cargo.toml index 398d9fe3..8bea154c 100644 --- a/crates/cdk-sqlite/Cargo.toml +++ b/crates/cdk-sqlite/Cargo.toml @@ -19,7 +19,7 @@ wallet = ["cdk/wallet"] async-trait = "0.1" cdk = { path = "../cdk", version = "0.5.0", default-features = false } bitcoin = { version = "0.32.2", default-features = false } -sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] } +sqlx = { version = "0.6.3", default-features = false, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "uuid"] } thiserror = "1" tokio = { version = "1", features = [ "time", @@ -29,3 +29,4 @@ tokio = { version = "1", features = [ tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } serde_json = "1" lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } +uuid = { version = "1", features = ["v4", "serde"] } diff --git a/crates/cdk-sqlite/src/mint/mod.rs b/crates/cdk-sqlite/src/mint/mod.rs index 2a5d369a..203b1ab5 100644 --- a/crates/cdk-sqlite/src/mint/mod.rs +++ b/crates/cdk-sqlite/src/mint/mod.rs @@ -23,6 +23,8 @@ use error::Error; use lightning_invoice::Bolt11Invoice; use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions, SqliteRow}; use sqlx::Row; +use uuid::fmt::Hyphenated; +use uuid::Uuid; pub mod error; @@ -236,7 +238,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?); } } - async fn get_mint_quote(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_mint_quote(&self, quote_id: &Uuid) -> Result, Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; let rec = sqlx::query( r#" @@ -245,7 +247,7 @@ FROM mint_quote WHERE id=?; "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .fetch_one(&mut transaction) .await; @@ -345,7 +347,7 @@ WHERE request_lookup_id=?; async fn update_mint_quote_state( &self, - quote_id: &str, + quote_id: &Uuid, state: MintQuoteState, ) -> Result { let mut transaction = self.pool.begin().await.map_err(Error::from)?; @@ -357,7 +359,7 @@ FROM mint_quote WHERE id=?; "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .fetch_one(&mut transaction) .await; let quote = match rec { @@ -378,7 +380,7 @@ WHERE id=?; "#, ) .bind(state.to_string()) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .execute(&mut transaction) .await; @@ -430,7 +432,7 @@ FROM mint_quote } } - async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> { + async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; let res = sqlx::query( @@ -439,7 +441,7 @@ DELETE FROM mint_quote WHERE id=? "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .execute(&mut transaction) .await; @@ -497,7 +499,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?); } } } - async fn get_melt_quote(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_melt_quote(&self, quote_id: &Uuid) -> Result, Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; let rec = sqlx::query( r#" @@ -506,7 +508,7 @@ FROM melt_quote WHERE id=?; "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .fetch_one(&mut transaction) .await; @@ -564,7 +566,7 @@ FROM melt_quote async fn update_melt_quote_state( &self, - quote_id: &str, + quote_id: &Uuid, state: MeltQuoteState, ) -> Result { let mut transaction = self.pool.begin().await.map_err(Error::from)?; @@ -576,7 +578,7 @@ FROM melt_quote WHERE id=?; "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .fetch_one(&mut transaction) .await; @@ -598,7 +600,7 @@ WHERE id=?; "#, ) .bind(state.to_string()) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .execute(&mut transaction) .await; @@ -619,7 +621,7 @@ WHERE id=?; Ok(quote.state) } - async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> { + async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; let res = sqlx::query( r#" @@ -627,7 +629,7 @@ DELETE FROM melt_quote WHERE id=? "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .execute(&mut transaction) .await; @@ -748,7 +750,7 @@ FROM keyset; } } - async fn add_proofs(&self, proofs: Proofs, quote_id: Option) -> Result<(), Self::Err> { + async fn add_proofs(&self, proofs: Proofs, quote_id: Option) -> Result<(), Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; for proof in proofs { if let Err(err) = sqlx::query( @@ -765,7 +767,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?); .bind(proof.c.to_bytes().to_vec()) .bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap())) .bind("UNSPENT") - .bind(quote_id.clone()) + .bind(quote_id.map(|q| q.hyphenated())) .execute(&mut transaction) .await .map_err(Error::from) @@ -812,7 +814,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?); Ok(ys.iter().map(|y| proofs.remove(y)).collect()) } - async fn get_proof_ys_by_quote_id(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result, Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; let rec = sqlx::query( @@ -822,7 +824,7 @@ FROM proof WHERE quote_id=?; "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .fetch_all(&mut transaction) .await; @@ -996,7 +998,7 @@ WHERE keyset_id=?; &self, blinded_messages: &[PublicKey], blinded_signatures: &[BlindSignature], - quote_id: Option, + quote_id: Option, ) -> Result<(), Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; for (message, signature) in blinded_messages.iter().zip(blinded_signatures) { @@ -1011,7 +1013,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?); .bind(u64::from(signature.amount) as i64) .bind(signature.keyset_id.to_string()) .bind(signature.c.to_bytes().to_vec()) - .bind(quote_id.clone()) + .bind(quote_id.map(|q| q.hyphenated())) .bind(signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex())) .bind(signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex())) .execute(&mut transaction) @@ -1111,7 +1113,7 @@ WHERE keyset_id=?; async fn add_melt_request( &self, - melt_request: MeltBolt11Request, + melt_request: MeltBolt11Request, ln_key: LnKey, ) -> Result<(), Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; @@ -1149,8 +1151,8 @@ VALUES (?, ?, ?, ?, ?); async fn get_melt_request( &self, - quote_id: &str, - ) -> Result, Self::Err> { + quote_id: &Uuid, + ) -> Result, LnKey)>, Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; let rec = sqlx::query( @@ -1160,7 +1162,7 @@ FROM melt_request WHERE id=?; "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .fetch_one(&mut transaction) .await; @@ -1192,7 +1194,7 @@ WHERE id=?; /// Get [`BlindSignature`]s for quote async fn get_blind_signatures_for_quote( &self, - quote_id: &str, + quote_id: &Uuid, ) -> Result, Self::Err> { let mut transaction = self.pool.begin().await.map_err(Error::from)?; @@ -1203,7 +1205,7 @@ FROM blind_signature WHERE quote_id=?; "#, ) - .bind(quote_id) + .bind(quote_id.as_hyphenated()) .fetch_all(&mut transaction) .await; @@ -1254,7 +1256,7 @@ fn sqlite_row_to_keyset_info(row: SqliteRow) -> Result { } fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result { - let row_id: String = row.try_get("id").map_err(Error::from)?; + let row_id: Hyphenated = row.try_get("id").map_err(Error::from)?; let row_mint_url: String = row.try_get("mint_url").map_err(Error::from)?; let row_amount: i64 = row.try_get("amount").map_err(Error::from)?; let row_unit: String = row.try_get("unit").map_err(Error::from)?; @@ -1273,7 +1275,7 @@ fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result { }; Ok(MintQuote { - id: row_id, + id: row_id.into_uuid(), mint_url: MintUrl::from_str(&row_mint_url)?, amount: Amount::from(row_amount as u64), unit: CurrencyUnit::from_str(&row_unit).map_err(Error::from)?, @@ -1285,7 +1287,7 @@ fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result { } fn sqlite_row_to_melt_quote(row: SqliteRow) -> Result { - let row_id: String = row.try_get("id").map_err(Error::from)?; + let row_id: Hyphenated = row.try_get("id").map_err(Error::from)?; let row_unit: String = row.try_get("unit").map_err(Error::from)?; let row_amount: i64 = row.try_get("amount").map_err(Error::from)?; let row_request: String = row.try_get("request").map_err(Error::from)?; @@ -1299,7 +1301,7 @@ fn sqlite_row_to_melt_quote(row: SqliteRow) -> Result { let request_lookup_id = row_request_lookup.unwrap_or(row_request.clone()); Ok(mint::MeltQuote { - id: row_id, + id: row_id.into_uuid(), amount: Amount::from(row_amount as u64), unit: CurrencyUnit::from_str(&row_unit).map_err(Error::from)?, request: row_request, @@ -1375,15 +1377,15 @@ fn sqlite_row_to_blind_signature(row: SqliteRow) -> Result Result<(MeltBolt11Request, LnKey), Error> { - let quote_id: String = row.try_get("id").map_err(Error::from)?; +fn sqlite_row_to_melt_request(row: SqliteRow) -> Result<(MeltBolt11Request, LnKey), Error> { + let quote_id: Hyphenated = row.try_get("id").map_err(Error::from)?; let row_inputs: String = row.try_get("inputs").map_err(Error::from)?; let row_outputs: Option = row.try_get("outputs").map_err(Error::from)?; let row_method: String = row.try_get("method").map_err(Error::from)?; let row_unit: String = row.try_get("unit").map_err(Error::from)?; let melt_request = MeltBolt11Request { - quote: quote_id, + quote: quote_id.into_uuid(), inputs: serde_json::from_str(&row_inputs)?, outputs: row_outputs.and_then(|o| serde_json::from_str(&o).ok()), }; diff --git a/crates/cdk/Cargo.toml b/crates/cdk/Cargo.toml index daf0c009..c5f97b11 100644 --- a/crates/cdk/Cargo.toml +++ b/crates/cdk/Cargo.toml @@ -42,7 +42,7 @@ thiserror = "1" futures = { version = "0.3.28", default-features = false, optional = true, features = ["alloc"] } url = "2.3" utoipa = { version = "4", optional = true } -uuid = { version = "1", features = ["v4"] } +uuid = { version = "1", features = ["v4", "serde"] } # -Z minimal-versions sync_wrapper = "0.1.2" diff --git a/crates/cdk/src/cdk_database/mint_memory.rs b/crates/cdk/src/cdk_database/mint_memory.rs index 3b4e1e79..65df8363 100644 --- a/crates/cdk/src/cdk_database/mint_memory.rs +++ b/crates/cdk/src/cdk_database/mint_memory.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use async_trait::async_trait; use tokio::sync::{Mutex, RwLock}; +use uuid::Uuid; use super::{Error, MintDatabase}; use crate::dhke::hash_to_curve; @@ -19,17 +20,18 @@ use crate::types::LnKey; /// Mint Memory Database #[derive(Debug, Clone, Default)] +#[allow(clippy::type_complexity)] pub struct MintMemoryDatabase { active_keysets: Arc>>, keysets: Arc>>, - mint_quotes: Arc>>, - melt_quotes: Arc>>, + mint_quotes: Arc>>, + melt_quotes: Arc>>, proofs: Arc>>, proof_state: Arc>>, - quote_proofs: Arc>>>, + quote_proofs: Arc>>>, blinded_signatures: Arc>>, - quote_signatures: Arc>>>, - melt_requests: Arc>>, + quote_signatures: Arc>>>, + melt_requests: Arc, LnKey)>>>, } impl MintMemoryDatabase { @@ -42,10 +44,10 @@ impl MintMemoryDatabase { melt_quotes: Vec, pending_proofs: Proofs, spent_proofs: Proofs, - quote_proofs: HashMap>, + quote_proofs: HashMap>, blinded_signatures: HashMap<[u8; 33], BlindSignature>, - quote_signatures: HashMap>, - melt_request: Vec<(MeltBolt11Request, LnKey)>, + quote_signatures: HashMap>, + melt_request: Vec<(MeltBolt11Request, LnKey)>, ) -> Result { let mut proofs = HashMap::new(); let mut proof_states = HashMap::new(); @@ -64,7 +66,7 @@ impl MintMemoryDatabase { let melt_requests = melt_request .into_iter() - .map(|(request, ln_key)| (request.quote.clone(), (request, ln_key))) + .map(|(request, ln_key)| (request.quote, (request, ln_key))) .collect(); Ok(Self { @@ -73,10 +75,10 @@ impl MintMemoryDatabase { keysets.into_iter().map(|k| (k.id, k)).collect(), )), mint_quotes: Arc::new(RwLock::new( - mint_quotes.into_iter().map(|q| (q.id.clone(), q)).collect(), + mint_quotes.into_iter().map(|q| (q.id, q)).collect(), )), melt_quotes: Arc::new(RwLock::new( - melt_quotes.into_iter().map(|q| (q.id.clone(), q)).collect(), + melt_quotes.into_iter().map(|q| (q.id, q)).collect(), )), proofs: Arc::new(RwLock::new(proofs)), proof_state: Arc::new(Mutex::new(proof_states)), @@ -119,20 +121,17 @@ impl MintDatabase for MintMemoryDatabase { } async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> { - self.mint_quotes - .write() - .await - .insert(quote.id.clone(), quote); + self.mint_quotes.write().await.insert(quote.id, quote); Ok(()) } - async fn get_mint_quote(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_mint_quote(&self, quote_id: &Uuid) -> Result, Self::Err> { Ok(self.mint_quotes.read().await.get(quote_id).cloned()) } async fn update_mint_quote_state( &self, - quote_id: &str, + quote_id: &Uuid, state: MintQuoteState, ) -> Result { let mut mint_quotes = self.mint_quotes.write().await; @@ -146,7 +145,7 @@ impl MintDatabase for MintMemoryDatabase { quote.state = state; - mint_quotes.insert(quote_id.to_string(), quote.clone()); + mint_quotes.insert(*quote_id, quote.clone()); Ok(current_state) } @@ -186,27 +185,24 @@ impl MintDatabase for MintMemoryDatabase { Ok(self.mint_quotes.read().await.values().cloned().collect()) } - async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> { + async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> { self.mint_quotes.write().await.remove(quote_id); Ok(()) } async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err> { - self.melt_quotes - .write() - .await - .insert(quote.id.clone(), quote); + self.melt_quotes.write().await.insert(quote.id, quote); Ok(()) } - async fn get_melt_quote(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_melt_quote(&self, quote_id: &Uuid) -> Result, Self::Err> { Ok(self.melt_quotes.read().await.get(quote_id).cloned()) } async fn update_melt_quote_state( &self, - quote_id: &str, + quote_id: &Uuid, state: MeltQuoteState, ) -> Result { let mut melt_quotes = self.melt_quotes.write().await; @@ -220,7 +216,7 @@ impl MintDatabase for MintMemoryDatabase { quote.state = state; - melt_quotes.insert(quote_id.to_string(), quote.clone()); + melt_quotes.insert(*quote_id, quote.clone()); Ok(current_state) } @@ -229,7 +225,7 @@ impl MintDatabase for MintMemoryDatabase { Ok(self.melt_quotes.read().await.values().cloned().collect()) } - async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> { + async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err> { self.melt_quotes.write().await.remove(quote_id); Ok(()) @@ -237,18 +233,18 @@ impl MintDatabase for MintMemoryDatabase { async fn add_melt_request( &self, - melt_request: MeltBolt11Request, + melt_request: MeltBolt11Request, ln_key: LnKey, ) -> Result<(), Self::Err> { let mut melt_requests = self.melt_requests.write().await; - melt_requests.insert(melt_request.quote.clone(), (melt_request, ln_key)); + melt_requests.insert(melt_request.quote, (melt_request, ln_key)); Ok(()) } async fn get_melt_request( &self, - quote_id: &str, - ) -> Result, Self::Err> { + quote_id: &Uuid, + ) -> Result, LnKey)>, Self::Err> { let melt_requests = self.melt_requests.read().await; let melt_request = melt_requests.get(quote_id); @@ -256,7 +252,7 @@ impl MintDatabase for MintMemoryDatabase { Ok(melt_request.cloned()) } - async fn add_proofs(&self, proofs: Proofs, quote_id: Option) -> Result<(), Self::Err> { + async fn add_proofs(&self, proofs: Proofs, quote_id: Option) -> Result<(), Self::Err> { let mut db_proofs = self.proofs.write().await; let mut ys = Vec::with_capacity(proofs.capacity()); @@ -293,7 +289,7 @@ impl MintDatabase for MintMemoryDatabase { Ok(proofs) } - async fn get_proof_ys_by_quote_id(&self, quote_id: &str) -> Result, Self::Err> { + async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result, Self::Err> { let quote_proofs = &__self.quote_proofs.lock().await; match quote_proofs.get(quote_id) { @@ -360,7 +356,7 @@ impl MintDatabase for MintMemoryDatabase { &self, blinded_message: &[PublicKey], blind_signatures: &[BlindSignature], - quote_id: Option, + quote_id: Option, ) -> Result<(), Self::Err> { let mut current_blinded_signatures = self.blinded_signatures.write().await; @@ -370,7 +366,7 @@ impl MintDatabase for MintMemoryDatabase { if let Some(quote_id) = quote_id { let mut current_quote_signatures = self.quote_signatures.write().await; - current_quote_signatures.insert(quote_id.clone(), blind_signatures.to_vec()); + current_quote_signatures.insert(quote_id, blind_signatures.to_vec()); let t = current_quote_signatures.get("e_id); println!("after insert: {:?}", t); } @@ -411,7 +407,7 @@ impl MintDatabase for MintMemoryDatabase { /// Get [`BlindSignature`]s for quote async fn get_blind_signatures_for_quote( &self, - quote_id: &str, + quote_id: &Uuid, ) -> Result, Self::Err> { let ys = self.quote_signatures.read().await; diff --git a/crates/cdk/src/cdk_database/mod.rs b/crates/cdk/src/cdk_database/mod.rs index 76d5e7fc..a590a67c 100644 --- a/crates/cdk/src/cdk_database/mod.rs +++ b/crates/cdk/src/cdk_database/mod.rs @@ -7,6 +7,8 @@ use std::fmt::Debug; #[cfg(any(feature = "wallet", feature = "mint"))] use async_trait::async_trait; use thiserror::Error; +#[cfg(feature = "mint")] +use uuid::Uuid; #[cfg(feature = "mint")] use crate::mint; @@ -187,11 +189,11 @@ pub trait MintDatabase { /// Add [`MintMintQuote`] async fn add_mint_quote(&self, quote: MintMintQuote) -> Result<(), Self::Err>; /// Get [`MintMintQuote`] - async fn get_mint_quote(&self, quote_id: &str) -> Result, Self::Err>; + async fn get_mint_quote(&self, quote_id: &Uuid) -> Result, Self::Err>; /// Update state of [`MintMintQuote`] async fn update_mint_quote_state( &self, - quote_id: &str, + quote_id: &Uuid, state: MintQuoteState, ) -> Result; /// Get all [`MintMintQuote`]s @@ -207,34 +209,34 @@ pub trait MintDatabase { /// Get Mint Quotes async fn get_mint_quotes(&self) -> Result, Self::Err>; /// Remove [`MintMintQuote`] - async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err>; + async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>; /// Add [`mint::MeltQuote`] async fn add_melt_quote(&self, quote: mint::MeltQuote) -> Result<(), Self::Err>; /// Get [`mint::MeltQuote`] - async fn get_melt_quote(&self, quote_id: &str) -> Result, Self::Err>; + async fn get_melt_quote(&self, quote_id: &Uuid) -> Result, Self::Err>; /// Update [`mint::MeltQuote`] state async fn update_melt_quote_state( &self, - quote_id: &str, + quote_id: &Uuid, state: MeltQuoteState, ) -> Result; /// Get all [`mint::MeltQuote`]s async fn get_melt_quotes(&self) -> Result, Self::Err>; /// Remove [`mint::MeltQuote`] - async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err>; + async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Self::Err>; /// Add melt request async fn add_melt_request( &self, - melt_request: MeltBolt11Request, + melt_request: MeltBolt11Request, ln_key: LnKey, ) -> Result<(), Self::Err>; /// Get melt request async fn get_melt_request( &self, - quote_id: &str, - ) -> Result, Self::Err>; + quote_id: &Uuid, + ) -> Result, LnKey)>, Self::Err>; /// Add [`MintKeySetInfo`] async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err>; @@ -244,11 +246,11 @@ pub trait MintDatabase { async fn get_keyset_infos(&self) -> Result, Self::Err>; /// Add spent [`Proofs`] - async fn add_proofs(&self, proof: Proofs, quote_id: Option) -> Result<(), Self::Err>; + async fn add_proofs(&self, proof: Proofs, quote_id: Option) -> Result<(), Self::Err>; /// Get [`Proofs`] by ys async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result>, Self::Err>; /// Get ys by quote id - async fn get_proof_ys_by_quote_id(&self, quote_id: &str) -> Result, Self::Err>; + async fn get_proof_ys_by_quote_id(&self, quote_id: &Uuid) -> Result, Self::Err>; /// Get [`Proofs`] state async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result>, Self::Err>; /// Get [`Proofs`] state @@ -268,7 +270,7 @@ pub trait MintDatabase { &self, blinded_messages: &[PublicKey], blind_signatures: &[BlindSignature], - quote_id: Option, + quote_id: Option, ) -> Result<(), Self::Err>; /// Get [`BlindSignature`]s async fn get_blind_signatures( @@ -283,6 +285,6 @@ pub trait MintDatabase { /// Get [`BlindSignature`]s for quote async fn get_blind_signatures_for_quote( &self, - quote_id: &str, + quote_id: &Uuid, ) -> Result, Self::Err>; } diff --git a/crates/cdk/src/mint/melt.rs b/crates/cdk/src/mint/melt.rs index 8ac01d69..55add5a1 100644 --- a/crates/cdk/src/mint/melt.rs +++ b/crates/cdk/src/mint/melt.rs @@ -4,6 +4,7 @@ use std::str::FromStr; use anyhow::bail; use lightning_invoice::Bolt11Invoice; use tracing::instrument; +use uuid::Uuid; use super::{ CurrencyUnit, MeltBolt11Request, MeltQuote, MeltQuoteBolt11Request, MeltQuoteBolt11Response, @@ -53,7 +54,7 @@ impl Mint { pub async fn get_melt_bolt11_quote( &self, melt_request: &MeltQuoteBolt11Request, - ) -> Result { + ) -> Result, Error> { let MeltQuoteBolt11Request { request, unit, @@ -117,7 +118,10 @@ impl Mint { /// Check melt quote status #[instrument(skip(self))] - pub async fn check_melt_quote(&self, quote_id: &str) -> Result { + pub async fn check_melt_quote( + &self, + quote_id: &Uuid, + ) -> Result, Error> { let quote = self .localstore .get_melt_quote(quote_id) @@ -159,7 +163,7 @@ impl Mint { /// Remove melt quote #[instrument(skip(self))] - pub async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Error> { + pub async fn remove_melt_quote(&self, quote_id: &Uuid) -> Result<(), Error> { self.localstore.remove_melt_quote(quote_id).await?; Ok(()) @@ -170,7 +174,7 @@ impl Mint { pub async fn check_melt_expected_ln_fees( &self, melt_quote: &MeltQuote, - melt_request: &MeltBolt11Request, + melt_request: &MeltBolt11Request, ) -> Result, Error> { let invoice = Bolt11Invoice::from_str(&melt_quote.request)?; @@ -226,7 +230,7 @@ impl Mint { #[instrument(skip_all)] pub async fn verify_melt_request( &self, - melt_request: &MeltBolt11Request, + melt_request: &MeltBolt11Request, ) -> Result { let state = self .localstore @@ -248,10 +252,7 @@ impl Mint { } self.localstore - .add_proofs( - melt_request.inputs.clone(), - Some(melt_request.quote.clone()), - ) + .add_proofs(melt_request.inputs.clone(), Some(melt_request.quote)) .await?; self.check_ys_spendable(&ys, State::Pending).await?; @@ -345,7 +346,10 @@ impl Mint { /// made The [`Proofs`] should be returned to an unspent state and the /// quote should be unpaid #[instrument(skip_all)] - pub async fn process_unpaid_melt(&self, melt_request: &MeltBolt11Request) -> Result<(), Error> { + pub async fn process_unpaid_melt( + &self, + melt_request: &MeltBolt11Request, + ) -> Result<(), Error> { let input_ys = melt_request.inputs.ys()?; self.localstore @@ -373,8 +377,8 @@ impl Mint { #[instrument(skip_all)] pub async fn melt_bolt11( &self, - melt_request: &MeltBolt11Request, - ) -> Result { + melt_request: &MeltBolt11Request, + ) -> Result, Error> { use std::sync::Arc; async fn check_payment_state( ln: Arc + Send + Sync>, @@ -584,10 +588,10 @@ impl Mint { #[instrument(skip_all)] pub async fn process_melt_request( &self, - melt_request: &MeltBolt11Request, + melt_request: &MeltBolt11Request, payment_preimage: Option, total_spent: Amount, - ) -> Result { + ) -> Result, Error> { tracing::debug!("Processing melt quote: {}", melt_request.quote); let quote = self @@ -673,7 +677,7 @@ impl Mint { .map(|o| o.blinded_secret) .collect::>(), &change_sigs, - Some(quote.id.clone()), + Some(quote.id), ) .await?; diff --git a/crates/cdk/src/mint/mint_nut04.rs b/crates/cdk/src/mint/mint_nut04.rs index 5631fd04..5b401292 100644 --- a/crates/cdk/src/mint/mint_nut04.rs +++ b/crates/cdk/src/mint/mint_nut04.rs @@ -1,4 +1,5 @@ use tracing::instrument; +use uuid::Uuid; use super::{ nut04, CurrencyUnit, Mint, MintQuote, MintQuoteBolt11Request, MintQuoteBolt11Response, @@ -59,7 +60,7 @@ impl Mint { pub async fn get_mint_bolt11_quote( &self, mint_quote_request: MintQuoteBolt11Request, - ) -> Result { + ) -> Result, Error> { let MintQuoteBolt11Request { amount, unit, @@ -116,7 +117,7 @@ impl Mint { self.localstore.add_mint_quote(quote.clone()).await?; - let quote: MintQuoteBolt11Response = quote.into(); + let quote: MintQuoteBolt11Response = quote.into(); self.pubsub_manager .broadcast(NotificationPayload::MintQuoteBolt11Response(quote.clone())); @@ -126,7 +127,10 @@ impl Mint { /// Check mint quote #[instrument(skip(self))] - pub async fn check_mint_quote(&self, quote_id: &str) -> Result { + pub async fn check_mint_quote( + &self, + quote_id: &Uuid, + ) -> Result, Error> { let quote = self .localstore .get_mint_quote(quote_id) @@ -187,7 +191,7 @@ impl Mint { /// Remove mint quote #[instrument(skip_all)] - pub async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Error> { + pub async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Error> { self.localstore.remove_mint_quote(quote_id).await?; Ok(()) @@ -250,7 +254,7 @@ impl Mint { #[instrument(skip_all)] pub async fn process_mint_request( &self, - mint_request: nut04::MintBolt11Request, + mint_request: nut04::MintBolt11Request, ) -> Result { let mint_quote = if let Some(mint_quote) = self.localstore.get_mint_quote(&mint_request.quote).await? { @@ -321,7 +325,7 @@ impl Mint { .map(|p| p.blinded_secret) .collect::>(), &blind_signatures, - Some(mint_request.quote.clone()), + Some(mint_request.quote), ) .await?; diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index 3a1fc751..844a8d0d 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; use tokio::sync::{Notify, RwLock}; use tokio::task::JoinSet; use tracing::instrument; +use uuid::Uuid; use crate::cdk_database::{self, MintDatabase}; use crate::cdk_lightning::{self, MintLightning}; @@ -380,7 +381,7 @@ impl Mint { pub async fn handle_internal_melt_mint( &self, melt_quote: &MeltQuote, - melt_request: &MeltBolt11Request, + melt_request: &MeltBolt11Request, ) -> Result, Error> { let mint_quote = match self .localstore @@ -608,6 +609,7 @@ mod tests { use bitcoin::Network; use secp256k1::Secp256k1; + use uuid::Uuid; use super::*; use crate::types::LnKey; @@ -709,13 +711,13 @@ mod tests { pending_proofs: Proofs, spent_proofs: Proofs, blinded_signatures: HashMap<[u8; 33], BlindSignature>, - quote_proofs: HashMap>, - quote_signatures: HashMap>, + quote_proofs: HashMap>, + quote_signatures: HashMap>, mint_url: &'a str, seed: &'a [u8], mint_info: MintInfo, supported_units: HashMap, - melt_requests: Vec<(MeltBolt11Request, LnKey)>, + melt_requests: Vec<(MeltBolt11Request, LnKey)>, quote_ttl: QuoteTTL, } diff --git a/crates/cdk/src/mint/types.rs b/crates/cdk/src/mint/types.rs index 44047fd9..ad099c4c 100644 --- a/crates/cdk/src/mint/types.rs +++ b/crates/cdk/src/mint/types.rs @@ -12,7 +12,7 @@ use crate::Amount; #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct MintQuote { /// Quote id - pub id: String, + pub id: Uuid, /// Mint Url pub mint_url: MintUrl, /// Amount of quote @@ -43,7 +43,7 @@ impl MintQuote { Self { mint_url, - id: id.to_string(), + id, amount, unit, request, @@ -58,7 +58,7 @@ impl MintQuote { #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct MeltQuote { /// Quote id - pub id: String, + pub id: Uuid, /// Quote unit pub unit: CurrencyUnit, /// Quote amount @@ -90,7 +90,7 @@ impl MeltQuote { let id = Uuid::new_v4(); Self { - id: id.to_string(), + id, amount, unit, request, diff --git a/crates/cdk/src/nuts/nut04.rs b/crates/cdk/src/nuts/nut04.rs index 40a6f8d4..7f90ba32 100644 --- a/crates/cdk/src/nuts/nut04.rs +++ b/crates/cdk/src/nuts/nut04.rs @@ -5,8 +5,11 @@ use std::fmt; use std::str::FromStr; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use thiserror::Error; +#[cfg(feature = "mint")] +use uuid::Uuid; use super::nut00::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod}; use super::MintQuoteState; @@ -81,9 +84,10 @@ impl FromStr for QuoteState { /// Mint quote response [NUT-04] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] -pub struct MintQuoteBolt11Response { +#[serde(bound = "Q: Serialize + DeserializeOwned")] +pub struct MintQuoteBolt11Response { /// Quote Id - pub quote: String, + pub quote: Q, /// Payment request to fulfil pub request: String, /// Quote State @@ -93,8 +97,8 @@ pub struct MintQuoteBolt11Response { } #[cfg(feature = "mint")] -impl From for MintQuoteBolt11Response { - fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response { +impl From for MintQuoteBolt11Response { + fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response { MintQuoteBolt11Response { quote: mint_quote.id, request: mint_quote.request, @@ -107,16 +111,17 @@ impl From for MintQuoteBolt11Response { /// Mint request [NUT-04] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] -pub struct MintBolt11Request { +#[serde(bound = "Q: Serialize + DeserializeOwned")] +pub struct MintBolt11Request { /// Quote id #[cfg_attr(feature = "swagger", schema(max_length = 1_000))] - pub quote: String, + pub quote: Q, /// Outputs #[cfg_attr(feature = "swagger", schema(max_items = 1_000))] pub outputs: Vec, } -impl MintBolt11Request { +impl MintBolt11Request { /// Total [`Amount`] of outputs pub fn total_amount(&self) -> Result { Amount::try_sum( diff --git a/crates/cdk/src/nuts/nut05.rs b/crates/cdk/src/nuts/nut05.rs index df9064ef..3510ef4f 100644 --- a/crates/cdk/src/nuts/nut05.rs +++ b/crates/cdk/src/nuts/nut05.rs @@ -5,9 +5,12 @@ use std::fmt; use std::str::FromStr; +use serde::de::DeserializeOwned; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; use thiserror::Error; +#[cfg(feature = "mint")] +use uuid::Uuid; use super::nut00::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod, Proofs}; use super::nut15::Mpp; @@ -88,9 +91,10 @@ impl FromStr for QuoteState { /// Melt quote response [NUT-05] #[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] -pub struct MeltQuoteBolt11Response { +#[serde(bound = "Q: Serialize")] +pub struct MeltQuoteBolt11Response { /// Quote Id - pub quote: String, + pub quote: Q, /// The amount that needs to be provided pub amount: Amount, /// The fee reserve that is required @@ -112,10 +116,10 @@ pub struct MeltQuoteBolt11Response { } #[cfg(feature = "mint")] -impl From<&MeltQuote> for MeltQuoteBolt11Response { - fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response { +impl From<&MeltQuote> for MeltQuoteBolt11Response { + fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response { MeltQuoteBolt11Response { - quote: melt_quote.id.clone(), + quote: melt_quote.id, payment_preimage: None, change: None, state: melt_quote.state, @@ -129,14 +133,14 @@ impl From<&MeltQuote> for MeltQuoteBolt11Response { // A custom deserializer is needed until all mints // update some will return without the required state. -impl<'de> Deserialize<'de> for MeltQuoteBolt11Response { +impl<'de, Q: DeserializeOwned> Deserialize<'de> for MeltQuoteBolt11Response { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let value = Value::deserialize(deserializer)?; - let quote: String = serde_json::from_value( + let quote: Q = serde_json::from_value( value .get("quote") .ok_or(serde::de::Error::missing_field("quote"))? @@ -212,8 +216,8 @@ impl<'de> Deserialize<'de> for MeltQuoteBolt11Response { } #[cfg(feature = "mint")] -impl From for MeltQuoteBolt11Response { - fn from(melt_quote: mint::MeltQuote) -> MeltQuoteBolt11Response { +impl From for MeltQuoteBolt11Response { + fn from(melt_quote: mint::MeltQuote) -> MeltQuoteBolt11Response { let paid = melt_quote.state == QuoteState::Paid; MeltQuoteBolt11Response { quote: melt_quote.id, @@ -231,9 +235,10 @@ impl From for MeltQuoteBolt11Response { /// Melt Bolt11 Request [NUT-05] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))] -pub struct MeltBolt11Request { +#[serde(bound = "Q: Serialize + DeserializeOwned")] +pub struct MeltBolt11Request { /// Quote ID - pub quote: String, + pub quote: Q, /// Proofs #[cfg_attr(feature = "swagger", schema(value_type = Vec))] pub inputs: Proofs, @@ -242,7 +247,7 @@ pub struct MeltBolt11Request { pub outputs: Option>, } -impl MeltBolt11Request { +impl MeltBolt11Request { /// Total [`Amount`] of [`Proofs`] pub fn proofs_amount(&self) -> Result { Amount::try_sum(self.inputs.iter().map(|proof| proof.amount)) diff --git a/crates/cdk/src/nuts/nut08.rs b/crates/cdk/src/nuts/nut08.rs index 3f05bed3..64365432 100644 --- a/crates/cdk/src/nuts/nut08.rs +++ b/crates/cdk/src/nuts/nut08.rs @@ -5,7 +5,7 @@ use super::nut05::{MeltBolt11Request, MeltQuoteBolt11Response}; use crate::Amount; -impl MeltBolt11Request { +impl MeltBolt11Request { /// Total output [`Amount`] pub fn output_amount(&self) -> Option { self.outputs @@ -14,7 +14,7 @@ impl MeltBolt11Request { } } -impl MeltQuoteBolt11Response { +impl MeltQuoteBolt11Response { /// Total change [`Amount`] pub fn change_amount(&self) -> Option { self.change diff --git a/crates/cdk/src/nuts/nut17/mod.rs b/crates/cdk/src/nuts/nut17/mod.rs index 8fbdf413..424eb435 100644 --- a/crates/cdk/src/nuts/nut17/mod.rs +++ b/crates/cdk/src/nuts/nut17/mod.rs @@ -1,6 +1,7 @@ //! Specific Subscription for the cdk crate use std::ops::Deref; +use std::str::FromStr; use std::sync::Arc; use serde::{Deserialize, Serialize}; @@ -8,7 +9,9 @@ use serde::{Deserialize, Serialize}; mod on_subscription; pub use on_subscription::OnSubscription; +use uuid::Uuid; +use super::PublicKey; use crate::cdk_database::{self, MintDatabase}; use crate::nuts::{ BlindSignature, CurrencyUnit, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response, @@ -91,9 +94,9 @@ pub enum NotificationPayload { /// Proof State ProofState(ProofState), /// Melt Quote Bolt11 Response - MeltQuoteBolt11Response(MeltQuoteBolt11Response), + MeltQuoteBolt11Response(MeltQuoteBolt11Response), /// Mint Quote Bolt11 Response - MintQuoteBolt11Response(MintQuoteBolt11Response), + MintQuoteBolt11Response(MintQuoteBolt11Response), } impl From for NotificationPayload { @@ -102,37 +105,42 @@ impl From for NotificationPayload { } } -impl From for NotificationPayload { - fn from(melt_quote: MeltQuoteBolt11Response) -> NotificationPayload { +impl From> for NotificationPayload { + fn from(melt_quote: MeltQuoteBolt11Response) -> NotificationPayload { NotificationPayload::MeltQuoteBolt11Response(melt_quote) } } -impl From for NotificationPayload { - fn from(mint_quote: MintQuoteBolt11Response) -> NotificationPayload { +impl From> for NotificationPayload { + fn from(mint_quote: MintQuoteBolt11Response) -> NotificationPayload { NotificationPayload::MintQuoteBolt11Response(mint_quote) } } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +/// A parsed notification +pub enum Notification { + /// ProofState id is a Pubkey + ProofState(PublicKey), + /// MeltQuote id is an Uuid + MeltQuoteBolt11(Uuid), + /// MintQuote id is an Uuid + MintQuoteBolt11(Uuid), +} + impl Indexable for NotificationPayload { - type Type = (String, Kind); + type Type = Notification; fn to_indexes(&self) -> Vec> { match self { NotificationPayload::ProofState(proof_state) => { - vec![Index::from((proof_state.y.to_hex(), Kind::ProofState))] + vec![Index::from(Notification::ProofState(proof_state.y))] } NotificationPayload::MeltQuoteBolt11Response(melt_quote) => { - vec![Index::from(( - melt_quote.quote.clone(), - Kind::Bolt11MeltQuote, - ))] + vec![Index::from(Notification::MeltQuoteBolt11(melt_quote.quote))] } NotificationPayload::MintQuoteBolt11Response(mint_quote) => { - vec![Index::from(( - mint_quote.quote.clone(), - Kind::Bolt11MintQuote, - ))] + vec![Index::from(Notification::MintQuoteBolt11(mint_quote.quote))] } } } @@ -157,13 +165,27 @@ impl AsRef for Params { } } -impl From for Vec> { +impl From for Vec> { fn from(val: Params) -> Self { let sub_id: SubscriptionGlobalId = Default::default(); val.filters - .iter() - .map(|filter| Index::from(((filter.clone(), val.kind), val.id.clone(), sub_id))) - .collect() + .into_iter() + .map(|filter| { + let idx = match val.kind { + Kind::Bolt11MeltQuote => { + Notification::MeltQuoteBolt11(Uuid::from_str(&filter)?) + } + Kind::Bolt11MintQuote => { + Notification::MintQuoteBolt11(Uuid::from_str(&filter)?) + } + Kind::ProofState => Notification::ProofState(PublicKey::from_str(&filter)?), + }; + + Ok(Index::from((idx, val.id.clone(), sub_id))) + }) + .collect::>() + .unwrap() + // TODO don't unwrap, move to try from } } @@ -172,7 +194,7 @@ impl From for Vec> { /// /// Nut-17 implementation is system-wide and not only through the WebSocket, so /// it is possible for another part of the system to subscribe to events. -pub struct PubSubManager(pub_sub::Manager); +pub struct PubSubManager(pub_sub::Manager); #[allow(clippy::default_constructed_unit_structs)] impl Default for PubSubManager { @@ -188,7 +210,7 @@ impl From + Send + Sync>> for Pu } impl Deref for PubSubManager { - type Target = pub_sub::Manager; + type Target = pub_sub::Manager; fn deref(&self) -> &Self::Target { &self.0 @@ -202,7 +224,7 @@ impl PubSubManager { } /// Helper function to emit a MintQuoteBolt11Response status - pub fn mint_quote_bolt11_status>( + pub fn mint_quote_bolt11_status>>( &self, quote: E, new_state: MintQuoteState, @@ -214,7 +236,7 @@ impl PubSubManager { } /// Helper function to emit a MeltQuoteBolt11Response status - pub fn melt_quote_status>( + pub fn melt_quote_status>>( &self, quote: E, payment_preimage: Option, @@ -244,7 +266,11 @@ mod test { let manager = PubSubManager::default(); let params = Params { kind: Kind::ProofState, - filters: vec!["x".to_string()], + filters: vec![PublicKey::from_hex( + "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104", + ) + .unwrap() + .to_string()], id: "uno".into(), }; diff --git a/crates/cdk/src/nuts/nut17/on_subscription.rs b/crates/cdk/src/nuts/nut17/on_subscription.rs index ed3c634d..d7465567 100644 --- a/crates/cdk/src/nuts/nut17/on_subscription.rs +++ b/crates/cdk/src/nuts/nut17/on_subscription.rs @@ -1,10 +1,11 @@ //! On Subscription //! //! This module contains the code that is triggered when a new subscription is created. -use std::collections::HashMap; use std::sync::Arc; -use super::{Kind, NotificationPayload}; +use uuid::Uuid; + +use super::{Notification, NotificationPayload}; use crate::cdk_database::{self, MintDatabase}; use crate::nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey}; use crate::pub_sub::OnNewSubscription; @@ -22,7 +23,7 @@ pub struct OnSubscription( #[async_trait::async_trait] impl OnNewSubscription for OnSubscription { type Event = NotificationPayload; - type Index = (String, Kind); + type Index = Notification; async fn on_new_subscription( &self, @@ -35,76 +36,58 @@ impl OnNewSubscription for OnSubscription { }; let mut to_return = vec![]; + let mut public_keys: Vec = Vec::new(); + let mut melt_queries = Vec::new(); + let mut mint_queries = Vec::new(); - for (kind, values) in request.iter().fold( - HashMap::new(), - |mut acc: HashMap<&Kind, Vec<&String>>, (data, kind)| { - acc.entry(kind).or_default().push(data); - acc - }, - ) { - match kind { - Kind::Bolt11MeltQuote => { - let queries = values - .iter() - .map(|id| datastore.get_melt_quote(id)) - .collect::>(); - - to_return.extend( - futures::future::try_join_all(queries) - .await - .map(|quotes| { - quotes - .into_iter() - .filter_map(|quote| quote.map(|x| x.into())) - .map(|x: MeltQuoteBolt11Response| x.into()) - .collect::>() - }) - .map_err(|e| e.to_string())?, - ); + for idx in request.iter() { + match idx { + Notification::ProofState(pk) => public_keys.push(*pk), + Notification::MeltQuoteBolt11(uuid) => { + melt_queries.push(datastore.get_melt_quote(uuid)) } - Kind::Bolt11MintQuote => { - let queries = values - .iter() - .map(|id| datastore.get_mint_quote(id)) - .collect::>(); - - to_return.extend( - futures::future::try_join_all(queries) - .await - .map(|quotes| { - quotes - .into_iter() - .filter_map(|quote| quote.map(|x| x.into())) - .map(|x: MintQuoteBolt11Response| x.into()) - .collect::>() - }) - .map_err(|e| e.to_string())?, - ); - } - Kind::ProofState => { - let public_keys = values - .iter() - .map(PublicKey::from_hex) - .collect::, _>>() - .map_err(|e| e.to_string())?; - - to_return.extend( - datastore - .get_proofs_states(&public_keys) - .await - .map_err(|e| e.to_string())? - .into_iter() - .enumerate() - .filter_map(|(idx, state)| { - state.map(|state| (public_keys[idx], state).into()) - }) - .map(|state: ProofState| state.into()), - ); + Notification::MintQuoteBolt11(uuid) => { + mint_queries.push(datastore.get_mint_quote(uuid)) } } } + to_return.extend( + futures::future::try_join_all(melt_queries) + .await + .map(|quotes| { + quotes + .into_iter() + .filter_map(|quote| quote.map(|x| x.into())) + .map(|x: MeltQuoteBolt11Response| x.into()) + .collect::>() + }) + .map_err(|e| e.to_string())?, + ); + to_return.extend( + futures::future::try_join_all(mint_queries) + .await + .map(|quotes| { + quotes + .into_iter() + .filter_map(|quote| quote.map(|x| x.into())) + .map(|x: MintQuoteBolt11Response| x.into()) + .collect::>() + }) + .map_err(|e| e.to_string())?, + ); + + to_return.extend( + datastore + .get_proofs_states(public_keys.as_slice()) + .await + .map_err(|e| e.to_string())? + .into_iter() + .enumerate() + .filter_map(|(idx, state)| state.map(|state| (public_keys[idx], state).into())) + .map(|state: ProofState| state.into()), + ); + Ok(to_return) } } diff --git a/crates/cdk/src/wallet/client.rs b/crates/cdk/src/wallet/client.rs index 15aefa8c..52091d03 100644 --- a/crates/cdk/src/wallet/client.rs +++ b/crates/cdk/src/wallet/client.rs @@ -120,7 +120,7 @@ impl HttpClientMethods for HttpClient { &self, mint_url: MintUrl, request: MintQuoteBolt11Request, - ) -> Result { + ) -> Result, Error> { let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11"])?; let res = self @@ -132,7 +132,7 @@ impl HttpClientMethods for HttpClient { .text() .await?; - convert_http_response!(MintQuoteBolt11Response, res) + convert_http_response!(MintQuoteBolt11Response, res) } /// Mint Quote status @@ -141,12 +141,12 @@ impl HttpClientMethods for HttpClient { &self, mint_url: MintUrl, quote_id: &str, - ) -> Result { + ) -> Result, Error> { let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?; let res = self.inner.get(url).send().await?.text().await?; - convert_http_response!(MintQuoteBolt11Response, res) + convert_http_response!(MintQuoteBolt11Response, res) } /// Mint Tokens [NUT-04] @@ -154,7 +154,7 @@ impl HttpClientMethods for HttpClient { async fn post_mint( &self, mint_url: MintUrl, - request: MintBolt11Request, + request: MintBolt11Request, ) -> Result { let url = mint_url.join_paths(&["v1", "mint", "bolt11"])?; @@ -176,7 +176,7 @@ impl HttpClientMethods for HttpClient { &self, mint_url: MintUrl, request: MeltQuoteBolt11Request, - ) -> Result { + ) -> Result, Error> { let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11"])?; let res = self @@ -188,7 +188,7 @@ impl HttpClientMethods for HttpClient { .text() .await?; - convert_http_response!(MeltQuoteBolt11Response, res) + convert_http_response!(MeltQuoteBolt11Response, res) } /// Melt Quote Status @@ -197,12 +197,12 @@ impl HttpClientMethods for HttpClient { &self, mint_url: MintUrl, quote_id: &str, - ) -> Result { + ) -> Result, Error> { let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?; let res = self.inner.get(url).send().await?.text().await?; - convert_http_response!(MeltQuoteBolt11Response, res) + convert_http_response!(MeltQuoteBolt11Response, res) } /// Melt [NUT-05] @@ -211,8 +211,8 @@ impl HttpClientMethods for HttpClient { async fn post_melt( &self, mint_url: MintUrl, - request: MeltBolt11Request, - ) -> Result { + request: MeltBolt11Request, + ) -> Result, Error> { let url = mint_url.join_paths(&["v1", "melt", "bolt11"])?; let res = self @@ -224,7 +224,7 @@ impl HttpClientMethods for HttpClient { .text() .await?; - convert_http_response!(MeltQuoteBolt11Response, res) + convert_http_response!(MeltQuoteBolt11Response, res) } /// Swap Token [NUT-03] @@ -319,20 +319,20 @@ pub trait HttpClientMethods: Debug { &self, mint_url: MintUrl, request: MintQuoteBolt11Request, - ) -> Result; + ) -> Result, Error>; /// Mint Quote status async fn get_mint_quote_status( &self, mint_url: MintUrl, quote_id: &str, - ) -> Result; + ) -> Result, Error>; /// Mint Tokens [NUT-04] async fn post_mint( &self, mint_url: MintUrl, - request: MintBolt11Request, + request: MintBolt11Request, ) -> Result; /// Melt Quote [NUT-05] @@ -340,22 +340,22 @@ pub trait HttpClientMethods: Debug { &self, mint_url: MintUrl, request: MeltQuoteBolt11Request, - ) -> Result; + ) -> Result, Error>; /// Melt Quote Status async fn get_melt_quote_status( &self, mint_url: MintUrl, quote_id: &str, - ) -> Result; + ) -> Result, Error>; /// Melt [NUT-05] /// [Nut-08] Lightning fee return if outputs defined async fn post_melt( &self, mint_url: MintUrl, - request: MeltBolt11Request, - ) -> Result; + request: MeltBolt11Request, + ) -> Result, Error>; /// Split Token [NUT-06] async fn post_swap( diff --git a/crates/cdk/src/wallet/melt.rs b/crates/cdk/src/wallet/melt.rs index e8a0a0b5..def4b59d 100644 --- a/crates/cdk/src/wallet/melt.rs +++ b/crates/cdk/src/wallet/melt.rs @@ -95,7 +95,7 @@ impl Wallet { pub async fn melt_quote_status( &self, quote_id: &str, - ) -> Result { + ) -> Result, Error> { let response = self .client .get_melt_quote_status(self.mint_url.clone(), quote_id) diff --git a/crates/cdk/src/wallet/mint.rs b/crates/cdk/src/wallet/mint.rs index 5a18b5bb..31acd1a9 100644 --- a/crates/cdk/src/wallet/mint.rs +++ b/crates/cdk/src/wallet/mint.rs @@ -78,7 +78,7 @@ impl Wallet { let quote = MintQuote { mint_url, - id: quote_res.quote.clone(), + id: quote_res.quote, amount, unit: unit.clone(), request: quote_res.request, @@ -93,7 +93,10 @@ impl Wallet { /// Check mint quote status #[instrument(skip(self, quote_id))] - pub async fn mint_quote_state(&self, quote_id: &str) -> Result { + pub async fn mint_quote_state( + &self, + quote_id: &str, + ) -> Result, Error> { let response = self .client .get_mint_quote_status(self.mint_url.clone(), quote_id)