Explorar el Código

compatibility for migrating Nutshell Mints quote ids (#984)

lollerfirst hace 2 meses
padre
commit
f1118b1c7b
Se han modificado 33 ficheros con 446 adiciones y 187 borrados
  1. 0 1
      crates/cashu/Cargo.toml
  2. 104 0
      crates/cashu/src/lib.rs
  3. 7 5
      crates/cashu/src/nuts/nut04.rs
  4. 8 5
      crates/cashu/src/nuts/nut05.rs
  5. 7 1
      crates/cashu/src/nuts/nut11/mod.rs
  6. 11 11
      crates/cashu/src/nuts/nut17/mod.rs
  7. 3 2
      crates/cashu/src/nuts/nut20.rs
  8. 6 6
      crates/cashu/src/nuts/nut23.rs
  9. 4 4
      crates/cashu/src/nuts/nut24.rs
  10. 11 11
      crates/cdk-axum/src/bolt12_router.rs
  11. 13 13
      crates/cdk-axum/src/router_handlers.rs
  12. 2 2
      crates/cdk-axum/src/ws/mod.rs
  13. 17 11
      crates/cdk-common/src/database/mint/mod.rs
  14. 7 0
      crates/cdk-common/src/database/mod.rs
  15. 4 0
      crates/cdk-common/src/error.rs
  16. 2 0
      crates/cdk-common/src/lib.rs
  17. 19 18
      crates/cdk-common/src/mint.rs
  18. 15 9
      crates/cdk-common/src/subscription.rs
  19. 4 4
      crates/cdk-common/src/ws.rs
  20. 1 0
      crates/cdk-integration-tests/src/bin/start_regtest_mints.rs
  21. 9 12
      crates/cdk-integration-tests/src/init_pure_tests.rs
  22. 3 0
      crates/cdk-integration-tests/src/shared.rs
  23. 1 1
      crates/cdk-mint-rpc/src/proto/server.rs
  24. 4 1
      crates/cdk-mintd/src/config.rs
  25. 1 0
      crates/cdk-mintd/src/env_vars/common.rs
  26. 4 0
      crates/cdk-mintd/src/env_vars/info.rs
  27. 3 0
      crates/cdk-mintd/src/lib.rs
  28. 135 32
      crates/cdk-sql-common/src/mint/mod.rs
  29. 12 12
      crates/cdk/src/mint/issue/mod.rs
  30. 13 13
      crates/cdk/src/mint/melt.rs
  31. 2 2
      crates/cdk/src/mint/mod.rs
  32. 8 6
      crates/cdk/src/mint/subscription/manager.rs
  33. 6 5
      crates/cdk/src/mint/subscription/on_subscription.rs

+ 0 - 1
crates/cashu/Cargo.toml

@@ -44,4 +44,3 @@ uuid = { workspace = true, features = ["js"], optional = true }
 
 [dev-dependencies]
 bip39.workspace = true
-uuid.workspace = true

+ 104 - 0
crates/cashu/src/lib.rs

@@ -25,3 +25,107 @@ macro_rules! ensure_cdk {
         }
     };
 }
+
+#[cfg(feature = "mint")]
+/// Quote ID. The specifications only define a string but CDK uses Uuid, so we use an enum to port compatibility.
+pub mod quote_id {
+    use std::fmt;
+    use std::str::FromStr;
+
+    use bitcoin::base64::engine::general_purpose;
+    use bitcoin::base64::Engine as _;
+    use serde::{de, Deserialize, Deserializer, Serialize};
+    use thiserror::Error;
+    use uuid::Uuid;
+
+    /// Invalid UUID
+    #[derive(Debug, Error)]
+    pub enum QuoteIdError {
+        /// UUID Error
+        #[error("invalid UUID: {0}")]
+        Uuid(#[from] uuid::Error),
+        /// Invalid base64
+        #[error("invalid base64")]
+        Base64,
+        /// Invalid quote ID
+        #[error("neither a valid UUID nor a valid base64 string")]
+        InvalidQuoteId,
+    }
+
+    /// Mint Quote ID
+    #[derive(Serialize, Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
+    #[serde(untagged)]
+    pub enum QuoteId {
+        /// (Nutshell) base64 quote ID
+        BASE64(String),
+        /// UUID quote ID
+        UUID(Uuid),
+    }
+
+    impl QuoteId {
+        /// Create a new UUID-based MintQuoteId
+        pub fn new_uuid() -> Self {
+            Self::UUID(Uuid::new_v4())
+        }
+    }
+
+    impl From<Uuid> for QuoteId {
+        fn from(uuid: Uuid) -> Self {
+            Self::UUID(uuid)
+        }
+    }
+
+    impl fmt::Display for QuoteId {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            match self {
+                QuoteId::BASE64(s) => write!(f, "{}", s),
+                QuoteId::UUID(u) => write!(f, "{}", u),
+            }
+        }
+    }
+
+    impl FromStr for QuoteId {
+        type Err = QuoteIdError;
+
+        fn from_str(s: &str) -> Result<Self, Self::Err> {
+            // Try UUID first
+            if let Ok(u) = Uuid::parse_str(s) {
+                return Ok(QuoteId::UUID(u));
+            }
+
+            // Try base64: decode, then re-encode and compare to ensure canonical form
+            // Use the standard (URL/filename safe or standard) depending on your needed alphabet.
+            // Here we use standard base64.
+            match general_purpose::URL_SAFE.decode(s) {
+                Ok(_bytes) => Ok(QuoteId::BASE64(s.to_string())),
+                Err(_) => Err(QuoteIdError::InvalidQuoteId),
+            }
+        }
+    }
+
+    impl<'de> Deserialize<'de> for QuoteId {
+        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            // Deserialize as plain string first
+            let s = String::deserialize(deserializer)?;
+
+            // Try UUID first
+            if let Ok(u) = Uuid::parse_str(&s) {
+                return Ok(QuoteId::UUID(u));
+            }
+
+            if general_purpose::URL_SAFE.decode(&s).is_ok() {
+                return Ok(QuoteId::BASE64(s));
+            }
+
+            // Neither matched — return a helpful error
+            Err(de::Error::custom(format!(
+                "QuoteId must be either a UUID (e.g. {}) or a valid base64 string; got: {}",
+                Uuid::nil(),
+                s
+            )))
+        }
+    }
+}

+ 7 - 5
crates/cashu/src/nuts/nut04.rs

@@ -10,10 +10,12 @@ use serde::de::{self, DeserializeOwned, Deserializer, MapAccess, Visitor};
 use serde::ser::{SerializeStruct, Serializer};
 use serde::{Deserialize, Serialize};
 use thiserror::Error;
-#[cfg(feature = "mint")]
-use uuid::Uuid;
 
 use super::nut00::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod};
+#[cfg(feature = "mint")]
+use crate::quote_id::QuoteId;
+#[cfg(feature = "mint")]
+use crate::quote_id::QuoteIdError;
 use crate::Amount;
 
 /// NUT04 Error
@@ -44,12 +46,12 @@ pub struct MintRequest<Q> {
 }
 
 #[cfg(feature = "mint")]
-impl TryFrom<MintRequest<String>> for MintRequest<Uuid> {
-    type Error = uuid::Error;
+impl TryFrom<MintRequest<String>> for MintRequest<QuoteId> {
+    type Error = QuoteIdError;
 
     fn try_from(value: MintRequest<String>) -> Result<Self, Self::Error> {
         Ok(Self {
-            quote: Uuid::from_str(&value.quote)?,
+            quote: QuoteId::from_str(&value.quote)?,
             outputs: value.outputs,
             signature: value.signature,
         })

+ 8 - 5
crates/cashu/src/nuts/nut05.rs

@@ -9,11 +9,11 @@ use serde::de::{self, DeserializeOwned, Deserializer, MapAccess, Visitor};
 use serde::ser::{SerializeStruct, Serializer};
 use serde::{Deserialize, Serialize};
 use thiserror::Error;
-#[cfg(feature = "mint")]
-use uuid::Uuid;
 
 use super::nut00::{BlindedMessage, CurrencyUnit, PaymentMethod, Proofs};
 use super::ProofsMethods;
+#[cfg(feature = "mint")]
+use crate::quote_id::QuoteId;
 use crate::Amount;
 
 /// NUT05 Error
@@ -28,6 +28,9 @@ pub enum Error {
     /// Unsupported unit
     #[error("Unsupported unit")]
     UnsupportedUnit,
+    /// Invalid quote id
+    #[error("Invalid quote id")]
+    InvalidQuote,
 }
 
 /// Possible states of a quote
@@ -91,12 +94,12 @@ pub struct MeltRequest<Q> {
 }
 
 #[cfg(feature = "mint")]
-impl TryFrom<MeltRequest<String>> for MeltRequest<Uuid> {
-    type Error = uuid::Error;
+impl TryFrom<MeltRequest<String>> for MeltRequest<QuoteId> {
+    type Error = Error;
 
     fn try_from(value: MeltRequest<String>) -> Result<Self, Self::Error> {
         Ok(Self {
-            quote: Uuid::from_str(&value.quote)?,
+            quote: QuoteId::from_str(&value.quote).map_err(|_e| Error::InvalidQuote)?,
             inputs: value.inputs,
             outputs: value.outputs,
         })

+ 7 - 1
crates/cashu/src/nuts/nut11/mod.rs

@@ -1262,6 +1262,7 @@ impl<'de> Deserialize<'de> for Tag {
     }
 }
 
+#[cfg(feature = "mint")]
 #[cfg(test)]
 mod tests {
     use std::str::FromStr;
@@ -1270,6 +1271,7 @@ mod tests {
 
     use super::*;
     use crate::nuts::Id;
+    use crate::quote_id::QuoteId;
     use crate::secret::Secret;
     use crate::{Amount, BlindedMessage};
 
@@ -1514,7 +1516,11 @@ mod tests {
         let blinded_msg = create_test_blinded_msg(pubkey);
 
         // Create melt request
-        let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof], Some(vec![blinded_msg]));
+        let mut melt = MeltRequest::new(
+            QuoteId::UUID(Uuid::new_v4()),
+            vec![proof],
+            Some(vec![blinded_msg]),
+        );
 
         // Before signing, should fail verification
         assert!(

+ 11 - 11
crates/cashu/src/nuts/nut17/mod.rs

@@ -1,14 +1,14 @@
 //! Specific Subscription for the cdk crate
 use serde::de::DeserializeOwned;
 use serde::{Deserialize, Serialize};
-#[cfg(feature = "mint")]
-use uuid::Uuid;
 
 #[cfg(feature = "mint")]
 use super::PublicKey;
 use crate::nuts::{
     CurrencyUnit, MeltQuoteBolt11Response, MintQuoteBolt11Response, PaymentMethod, ProofState,
 };
+#[cfg(feature = "mint")]
+use crate::quote_id::{QuoteId, QuoteIdError};
 use crate::MintQuoteBolt12Response;
 
 pub mod ws;
@@ -154,14 +154,14 @@ impl<T> From<MintQuoteBolt11Response<T>> for NotificationPayload<T> {
 pub enum Notification {
     /// ProofState id is a Pubkey
     ProofState(PublicKey),
-    /// MeltQuote id is an Uuid
-    MeltQuoteBolt11(Uuid),
-    /// MintQuote id is an Uuid
-    MintQuoteBolt11(Uuid),
-    /// MintQuote id is an Uuid
-    MintQuoteBolt12(Uuid),
-    /// MintQuote id is an Uuid
-    MeltQuoteBolt12(Uuid),
+    /// MeltQuote id is an QuoteId
+    MeltQuoteBolt11(QuoteId),
+    /// MintQuote id is an QuoteId
+    MintQuoteBolt11(QuoteId),
+    /// MintQuote id is an QuoteId
+    MintQuoteBolt12(QuoteId),
+    /// MintQuote id is an QuoteId
+    MeltQuoteBolt12(QuoteId),
 }
 
 /// Kind
@@ -190,7 +190,7 @@ pub enum Error {
     #[cfg(feature = "mint")]
     #[error("Uuid Error: {0}")]
     /// Uuid Error
-    Uuid(#[from] uuid::Error),
+    QuoteId(#[from] QuoteIdError),
 
     #[error("PublicKey Error: {0}")]
     /// PublicKey Error

+ 3 - 2
crates/cashu/src/nuts/nut20.rs

@@ -74,8 +74,6 @@ where
 #[cfg(test)]
 mod tests {
 
-    use uuid::Uuid;
-
     use super::*;
 
     #[test]
@@ -111,8 +109,11 @@ mod tests {
         assert_eq!(expected_msg_to_sign, request_msg_to_sign);
     }
 
+    #[cfg(feature = "mint")]
     #[test]
     fn test_valid_signature() {
+        use uuid::Uuid;
+
         let pubkey = PublicKey::from_hex(
             "03d56ce4e446a85bbdaa547b4ec2b073d40ff802831352b8272b7dd7a4de5a7cac",
         )

+ 6 - 6
crates/cashu/src/nuts/nut23.rs

@@ -8,10 +8,10 @@ use serde::de::DeserializeOwned;
 use serde::{Deserialize, Deserializer, Serialize};
 use serde_json::Value;
 use thiserror::Error;
-#[cfg(feature = "mint")]
-use uuid::Uuid;
 
 use super::{BlindSignature, CurrencyUnit, MeltQuoteState, Mpp, PublicKey};
+#[cfg(feature = "mint")]
+use crate::quote_id::QuoteId;
 use crate::Amount;
 
 /// NUT023 Error
@@ -120,8 +120,8 @@ impl<Q: ToString> MintQuoteBolt11Response<Q> {
 }
 
 #[cfg(feature = "mint")]
-impl From<MintQuoteBolt11Response<Uuid>> for MintQuoteBolt11Response<String> {
-    fn from(value: MintQuoteBolt11Response<Uuid>) -> Self {
+impl From<MintQuoteBolt11Response<QuoteId>> for MintQuoteBolt11Response<String> {
+    fn from(value: MintQuoteBolt11Response<QuoteId>) -> Self {
         Self {
             quote: value.quote.to_string(),
             request: value.request,
@@ -293,8 +293,8 @@ impl<Q: ToString> MeltQuoteBolt11Response<Q> {
 }
 
 #[cfg(feature = "mint")]
-impl From<MeltQuoteBolt11Response<Uuid>> for MeltQuoteBolt11Response<String> {
-    fn from(value: MeltQuoteBolt11Response<Uuid>) -> Self {
+impl From<MeltQuoteBolt11Response<QuoteId>> for MeltQuoteBolt11Response<String> {
+    fn from(value: MeltQuoteBolt11Response<QuoteId>) -> Self {
         Self {
             quote: value.quote.to_string(),
             amount: value.amount,

+ 4 - 4
crates/cashu/src/nuts/nut24.rs

@@ -1,10 +1,10 @@
 //! Bolt12
 use serde::{Deserialize, Serialize};
 use thiserror::Error;
-#[cfg(feature = "mint")]
-use uuid::Uuid;
 
 use super::{CurrencyUnit, MeltOptions, PublicKey};
+#[cfg(feature = "mint")]
+use crate::quote_id::QuoteId;
 use crate::Amount;
 
 /// NUT18 Error
@@ -76,8 +76,8 @@ impl<Q: ToString> MintQuoteBolt12Response<Q> {
 }
 
 #[cfg(feature = "mint")]
-impl From<MintQuoteBolt12Response<Uuid>> for MintQuoteBolt12Response<String> {
-    fn from(value: MintQuoteBolt12Response<Uuid>) -> Self {
+impl From<MintQuoteBolt12Response<QuoteId>> for MintQuoteBolt12Response<String> {
+    fn from(value: MintQuoteBolt12Response<QuoteId>) -> Self {
         Self {
             quote: value.quote.to_string(),
             request: value.request,

+ 11 - 11
crates/cdk-axum/src/bolt12_router.rs

@@ -3,6 +3,7 @@ use axum::extract::{Json, Path, State};
 use axum::response::Response;
 #[cfg(feature = "swagger")]
 use cdk::error::ErrorResponse;
+use cdk::mint::QuoteId;
 #[cfg(feature = "auth")]
 use cdk::nuts::nut21::{Method, ProtectedEndpoint, RoutePath};
 use cdk::nuts::{
@@ -11,17 +12,16 @@ use cdk::nuts::{
 };
 use paste::paste;
 use tracing::instrument;
-use uuid::Uuid;
 
 #[cfg(feature = "auth")]
 use crate::auth::AuthHeader;
 use crate::{into_response, post_cache_wrapper, MintState};
 
-post_cache_wrapper!(post_mint_bolt12, MintRequest<Uuid>, MintResponse);
+post_cache_wrapper!(post_mint_bolt12, MintRequest<QuoteId>, MintResponse);
 post_cache_wrapper!(
     post_melt_bolt12,
-    MeltRequest<Uuid>,
-    MeltQuoteBolt11Response<Uuid>
+    MeltRequest<QuoteId>,
+    MeltQuoteBolt11Response<QuoteId>
 );
 
 #[cfg_attr(feature = "swagger", utoipa::path(
@@ -38,7 +38,7 @@ pub async fn post_mint_bolt12_quote(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
     Json(payload): Json<MintQuoteBolt12Request>,
-) -> Result<Json<MintQuoteBolt12Response<Uuid>>, Response> {
+) -> Result<Json<MintQuoteBolt12Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     {
         state
@@ -77,8 +77,8 @@ pub async fn post_mint_bolt12_quote(
 pub async fn get_check_mint_bolt12_quote(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
-    Path(quote_id): Path<Uuid>,
-) -> Result<Json<MintQuoteBolt12Response<Uuid>>, Response> {
+    Path(quote_id): Path<QuoteId>,
+) -> Result<Json<MintQuoteBolt12Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     {
         state
@@ -115,7 +115,7 @@ pub async fn get_check_mint_bolt12_quote(
 pub async fn post_mint_bolt12(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
-    Json(payload): Json<MintRequest<Uuid>>,
+    Json(payload): Json<MintRequest<QuoteId>>,
 ) -> Result<Json<MintResponse>, Response> {
     #[cfg(feature = "auth")]
     {
@@ -155,7 +155,7 @@ pub async fn post_melt_bolt12_quote(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
     Json(payload): Json<MeltQuoteBolt12Request>,
-) -> Result<Json<MeltQuoteBolt11Response<Uuid>>, Response> {
+) -> Result<Json<MeltQuoteBolt11Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     {
         state
@@ -193,8 +193,8 @@ pub async fn post_melt_bolt12_quote(
 pub async fn post_melt_bolt12(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
-    Json(payload): Json<MeltRequest<Uuid>>,
-) -> Result<Json<MeltQuoteBolt11Response<Uuid>>, Response> {
+    Json(payload): Json<MeltRequest<QuoteId>>,
+) -> Result<Json<MeltQuoteBolt11Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     {
         state

+ 13 - 13
crates/cdk-axum/src/router_handlers.rs

@@ -4,6 +4,7 @@ use axum::extract::{Json, Path, State};
 use axum::http::StatusCode;
 use axum::response::{IntoResponse, Response};
 use cdk::error::{ErrorCode, ErrorResponse};
+use cdk::mint::QuoteId;
 #[cfg(feature = "auth")]
 use cdk::nuts::nut21::{Method, ProtectedEndpoint, RoutePath};
 use cdk::nuts::{
@@ -15,7 +16,6 @@ use cdk::nuts::{
 use cdk::util::unix_time;
 use paste::paste;
 use tracing::instrument;
-use uuid::Uuid;
 
 #[cfg(feature = "auth")]
 use crate::auth::AuthHeader;
@@ -62,11 +62,11 @@ macro_rules! post_cache_wrapper {
 }
 
 post_cache_wrapper!(post_swap, SwapRequest, SwapResponse);
-post_cache_wrapper!(post_mint_bolt11, MintRequest<Uuid>, MintResponse);
+post_cache_wrapper!(post_mint_bolt11, MintRequest<QuoteId>, MintResponse);
 post_cache_wrapper!(
     post_melt_bolt11,
-    MeltRequest<Uuid>,
-    MeltQuoteBolt11Response<Uuid>
+    MeltRequest<QuoteId>,
+    MeltQuoteBolt11Response<QuoteId>
 );
 
 #[cfg_attr(feature = "swagger", utoipa::path(
@@ -152,7 +152,7 @@ pub(crate) async fn post_mint_bolt11_quote(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
     Json(payload): Json<MintQuoteBolt11Request>,
-) -> Result<Json<MintQuoteBolt11Response<Uuid>>, Response> {
+) -> Result<Json<MintQuoteBolt11Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     state
         .mint
@@ -191,8 +191,8 @@ pub(crate) async fn post_mint_bolt11_quote(
 pub(crate) async fn get_check_mint_bolt11_quote(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
-    Path(quote_id): Path<Uuid>,
-) -> Result<Json<MintQuoteBolt11Response<Uuid>>, Response> {
+    Path(quote_id): Path<QuoteId>,
+) -> Result<Json<MintQuoteBolt11Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     {
         state
@@ -244,7 +244,7 @@ pub(crate) async fn ws_handler(
 pub(crate) async fn post_mint_bolt11(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
-    Json(payload): Json<MintRequest<Uuid>>,
+    Json(payload): Json<MintRequest<QuoteId>>,
 ) -> Result<Json<MintResponse>, Response> {
     #[cfg(feature = "auth")]
     {
@@ -286,7 +286,7 @@ pub(crate) async fn post_melt_bolt11_quote(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
     Json(payload): Json<MeltQuoteBolt11Request>,
-) -> Result<Json<MeltQuoteBolt11Response<Uuid>>, Response> {
+) -> Result<Json<MeltQuoteBolt11Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     {
         state
@@ -327,8 +327,8 @@ pub(crate) async fn post_melt_bolt11_quote(
 pub(crate) async fn get_check_melt_bolt11_quote(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
-    Path(quote_id): Path<Uuid>,
-) -> Result<Json<MeltQuoteBolt11Response<Uuid>>, Response> {
+    Path(quote_id): Path<QuoteId>,
+) -> Result<Json<MeltQuoteBolt11Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     {
         state
@@ -370,8 +370,8 @@ pub(crate) async fn get_check_melt_bolt11_quote(
 pub(crate) async fn post_melt_bolt11(
     #[cfg(feature = "auth")] auth: AuthHeader,
     State(state): State<MintState>,
-    Json(payload): Json<MeltRequest<Uuid>>,
-) -> Result<Json<MeltQuoteBolt11Response<Uuid>>, Response> {
+    Json(payload): Json<MeltRequest<QuoteId>>,
+) -> Result<Json<MeltQuoteBolt11Response<QuoteId>>, Response> {
     #[cfg(feature = "auth")]
     {
         state

+ 2 - 2
crates/cdk-axum/src/ws/mod.rs

@@ -1,6 +1,7 @@
 use std::collections::HashMap;
 
 use axum::extract::ws::{Message, WebSocket};
+use cdk::mint::QuoteId;
 use cdk::nuts::nut17::NotificationPayload;
 use cdk::pub_sub::SubId;
 use cdk::ws::{
@@ -9,7 +10,6 @@ use cdk::ws::{
 };
 use futures::StreamExt;
 use tokio::sync::mpsc;
-use uuid::Uuid;
 
 use crate::MintState;
 
@@ -37,7 +37,7 @@ pub use error::WsError;
 pub struct WsContext {
     state: MintState,
     subscriptions: HashMap<SubId, tokio::task::JoinHandle<()>>,
-    publisher: mpsc::Sender<(SubId, NotificationPayload<Uuid>)>,
+    publisher: mpsc::Sender<(SubId, NotificationPayload<QuoteId>)>,
 }
 
 /// Main function for websocket connections

+ 17 - 11
crates/cdk-common/src/database/mint/mod.rs

@@ -3,6 +3,7 @@
 use std::collections::HashMap;
 
 use async_trait::async_trait;
+use cashu::quote_id::QuoteId;
 use cashu::{Amount, MintInfo};
 use uuid::Uuid;
 
@@ -64,25 +65,27 @@ pub trait QuotesTransaction<'a> {
     type Err: Into<Error> + From<Error>;
 
     /// Get [`MintMintQuote`] and lock it for update in this transaction
-    async fn get_mint_quote(&mut self, quote_id: &Uuid)
-        -> Result<Option<MintMintQuote>, Self::Err>;
+    async fn get_mint_quote(
+        &mut self,
+        quote_id: &QuoteId,
+    ) -> Result<Option<MintMintQuote>, Self::Err>;
     /// Add [`MintMintQuote`]
     async fn add_mint_quote(&mut self, quote: MintMintQuote) -> Result<(), Self::Err>;
     /// Increment amount paid [`MintMintQuote`]
     async fn increment_mint_quote_amount_paid(
         &mut self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
         amount_paid: Amount,
         payment_id: String,
     ) -> Result<Amount, Self::Err>;
     /// Increment amount paid [`MintMintQuote`]
     async fn increment_mint_quote_amount_issued(
         &mut self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
         amount_issued: Amount,
     ) -> Result<Amount, Self::Err>;
     /// Remove [`MintMintQuote`]
-    async fn remove_mint_quote(&mut self, quote_id: &Uuid) -> Result<(), Self::Err>;
+    async fn remove_mint_quote(&mut self, quote_id: &QuoteId) -> Result<(), Self::Err>;
     /// Get [`mint::MeltQuote`] and lock it for update in this transaction
     async fn get_melt_quote(
         &mut self,
@@ -94,7 +97,7 @@ pub trait QuotesTransaction<'a> {
     /// Updates the request lookup id for a melt quote
     async fn update_melt_quote_request_lookup_id(
         &mut self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
         new_request_lookup_id: &PaymentIdentifier,
     ) -> Result<(), Self::Err>;
 
@@ -103,7 +106,7 @@ pub trait QuotesTransaction<'a> {
     /// It is expected for this function to fail if the state is already set to the new state
     async fn update_melt_quote_state(
         &mut self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
         new_state: MeltQuoteState,
         payment_proof: Option<String>,
     ) -> Result<(MeltQuoteState, mint::MeltQuote), Self::Err>;
@@ -129,7 +132,7 @@ pub trait QuotesDatabase {
     type Err: Into<Error> + From<Error>;
 
     /// Get [`MintMintQuote`]
-    async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintMintQuote>, Self::Err>;
+    async fn get_mint_quote(&self, quote_id: &QuoteId) -> Result<Option<MintMintQuote>, Self::Err>;
 
     /// Get all [`MintMintQuote`]s
     async fn get_mint_quote_by_request(
@@ -144,7 +147,10 @@ pub trait QuotesDatabase {
     /// Get Mint Quotes
     async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, Self::Err>;
     /// Get [`mint::MeltQuote`]
-    async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err>;
+    async fn get_melt_quote(
+        &self,
+        quote_id: &QuoteId,
+    ) -> Result<Option<mint::MeltQuote>, Self::Err>;
     /// Get all [`mint::MeltQuote`]s
     async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err>;
 }
@@ -205,7 +211,7 @@ pub trait SignaturesTransaction<'a> {
         &mut self,
         blinded_messages: &[PublicKey],
         blind_signatures: &[BlindSignature],
-        quote_id: Option<Uuid>,
+        quote_id: Option<QuoteId>,
     ) -> Result<(), Self::Err>;
 
     /// Get [`BlindSignature`]s
@@ -234,7 +240,7 @@ pub trait SignaturesDatabase {
     /// Get [`BlindSignature`]s for quote
     async fn get_blind_signatures_for_quote(
         &self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
     ) -> Result<Vec<BlindSignature>, Self::Err>;
 }
 

+ 7 - 0
crates/cdk-common/src/database/mod.rs

@@ -126,6 +126,13 @@ pub enum Error {
     #[error(transparent)]
     #[cfg(feature = "auth")]
     NUT22(#[from] crate::nuts::nut22::Error),
+    /// NUT04 Error
+    #[error(transparent)]
+    NUT04(#[from] crate::nuts::nut04::Error),
+    /// Quote ID Error
+    #[error(transparent)]
+    #[cfg(feature = "mint")]
+    QuoteId(#[from] crate::quote_id::QuoteIdError),
     /// Serde Error
     #[error(transparent)]
     Serde(#[from] serde_json::Error),

+ 4 - 0
crates/cdk-common/src/error.rs

@@ -363,6 +363,10 @@ pub enum Error {
     /// NUT23 Error
     #[error(transparent)]
     NUT23(#[from] crate::nuts::nut23::Error),
+    /// Quote ID Error
+    #[error(transparent)]
+    #[cfg(feature = "mint")]
+    QuoteId(#[from] crate::quote_id::QuoteIdError),
     /// From slice error
     #[error(transparent)]
     TryFromSliceError(#[from] TryFromSliceError),

+ 2 - 0
crates/cdk-common/src/lib.rs

@@ -29,5 +29,7 @@ pub use bitcoin;
 pub use cashu::amount::{self, Amount};
 pub use cashu::lightning_invoice::{self, Bolt11Invoice};
 pub use cashu::nuts::{self, *};
+#[cfg(feature = "mint")]
+pub use cashu::quote_id::{self, *};
 pub use cashu::{dhke, ensure_cdk, mint_url, secret, util, SECP256K1};
 pub use error::Error;

+ 19 - 18
crates/cdk-common/src/mint.rs

@@ -1,6 +1,7 @@
 //! Mint types
 
 use bitcoin::bip32::DerivationPath;
+use cashu::quote_id::QuoteId;
 use cashu::util::unix_time;
 use cashu::{
     Bolt11Invoice, MeltOptions, MeltQuoteBolt11Response, MintQuoteBolt11Response,
@@ -19,7 +20,7 @@ use crate::{Amount, CurrencyUnit, Id, KeySetInfo, PublicKey};
 #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MintQuote {
     /// Quote id
-    pub id: Uuid,
+    pub id: QuoteId,
     /// Amount of quote
     pub amount: Option<Amount>,
     /// Unit of quote
@@ -56,7 +57,7 @@ impl MintQuote {
     /// Create new [`MintQuote`]
     #[allow(clippy::too_many_arguments)]
     pub fn new(
-        id: Option<Uuid>,
+        id: Option<QuoteId>,
         request: String,
         unit: CurrencyUnit,
         amount: Option<Amount>,
@@ -70,7 +71,7 @@ impl MintQuote {
         payments: Vec<IncomingPayment>,
         issuance: Vec<Issuance>,
     ) -> Self {
-        let id = id.unwrap_or(Uuid::new_v4());
+        let id = id.unwrap_or_else(QuoteId::new_uuid);
 
         Self {
             id,
@@ -230,7 +231,7 @@ impl Issuance {
 #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MeltQuote {
     /// Quote id
-    pub id: Uuid,
+    pub id: QuoteId,
     /// Quote unit
     pub unit: CurrencyUnit,
     /// Quote amount
@@ -277,7 +278,7 @@ impl MeltQuote {
         let id = Uuid::new_v4();
 
         Self {
-            id,
+            id: QuoteId::UUID(id),
             amount,
             unit,
             request,
@@ -336,10 +337,10 @@ impl From<MintKeySetInfo> for KeySetInfo {
     }
 }
 
-impl From<MintQuote> for MintQuoteBolt11Response<Uuid> {
-    fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response<Uuid> {
+impl From<MintQuote> for MintQuoteBolt11Response<QuoteId> {
+    fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response<QuoteId> {
         MintQuoteBolt11Response {
-            quote: mint_quote.id,
+            quote: mint_quote.id.clone(),
             state: mint_quote.state(),
             request: mint_quote.request,
             expiry: Some(mint_quote.expiry),
@@ -352,18 +353,18 @@ impl From<MintQuote> for MintQuoteBolt11Response<Uuid> {
 
 impl From<MintQuote> for MintQuoteBolt11Response<String> {
     fn from(quote: MintQuote) -> Self {
-        let quote: MintQuoteBolt11Response<Uuid> = quote.into();
+        let quote: MintQuoteBolt11Response<QuoteId> = quote.into();
 
         quote.into()
     }
 }
 
-impl TryFrom<crate::mint::MintQuote> for MintQuoteBolt12Response<Uuid> {
+impl TryFrom<crate::mint::MintQuote> for MintQuoteBolt12Response<QuoteId> {
     type Error = crate::Error;
 
     fn try_from(mint_quote: crate::mint::MintQuote) -> Result<Self, Self::Error> {
         Ok(MintQuoteBolt12Response {
-            quote: mint_quote.id,
+            quote: mint_quote.id.clone(),
             request: mint_quote.request,
             expiry: Some(mint_quote.expiry),
             amount_paid: mint_quote.amount_paid,
@@ -379,16 +380,16 @@ impl TryFrom<MintQuote> for MintQuoteBolt12Response<String> {
     type Error = crate::Error;
 
     fn try_from(quote: MintQuote) -> Result<Self, Self::Error> {
-        let quote: MintQuoteBolt12Response<Uuid> = quote.try_into()?;
+        let quote: MintQuoteBolt12Response<QuoteId> = quote.try_into()?;
 
         Ok(quote.into())
     }
 }
 
-impl From<&MeltQuote> for MeltQuoteBolt11Response<Uuid> {
-    fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response<Uuid> {
+impl From<&MeltQuote> for MeltQuoteBolt11Response<QuoteId> {
+    fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response<QuoteId> {
         MeltQuoteBolt11Response {
-            quote: melt_quote.id,
+            quote: melt_quote.id.clone(),
             payment_preimage: None,
             change: None,
             state: melt_quote.state,
@@ -402,11 +403,11 @@ impl From<&MeltQuote> for MeltQuoteBolt11Response<Uuid> {
     }
 }
 
-impl From<MeltQuote> for MeltQuoteBolt11Response<Uuid> {
-    fn from(melt_quote: MeltQuote) -> MeltQuoteBolt11Response<Uuid> {
+impl From<MeltQuote> for MeltQuoteBolt11Response<QuoteId> {
+    fn from(melt_quote: MeltQuote) -> MeltQuoteBolt11Response<QuoteId> {
         let paid = melt_quote.state == MeltQuoteState::Paid;
         MeltQuoteBolt11Response {
-            quote: melt_quote.id,
+            quote: melt_quote.id.clone(),
             amount: melt_quote.amount,
             fee_reserve: melt_quote.fee_reserve,
             paid: Some(paid),

+ 15 - 9
crates/cdk-common/src/subscription.rs

@@ -6,11 +6,11 @@ use cashu::nut17::{self};
 #[cfg(feature = "mint")]
 use cashu::nut17::{Error, Kind, Notification};
 #[cfg(feature = "mint")]
+use cashu::quote_id::QuoteId;
+#[cfg(feature = "mint")]
 use cashu::{NotificationPayload, PublicKey};
 #[cfg(feature = "mint")]
 use serde::{Deserialize, Serialize};
-#[cfg(feature = "mint")]
-use uuid::Uuid;
 
 #[cfg(feature = "mint")]
 use crate::pub_sub::index::{Index, Indexable, SubscriptionGlobalId};
@@ -45,14 +45,14 @@ impl TryFrom<IndexableParams> for Vec<Index<Notification>> {
             .map(|filter| {
                 let idx = match params.kind {
                     Kind::Bolt11MeltQuote => {
-                        Notification::MeltQuoteBolt11(Uuid::from_str(&filter)?)
+                        Notification::MeltQuoteBolt11(QuoteId::from_str(&filter)?)
                     }
                     Kind::Bolt11MintQuote => {
-                        Notification::MintQuoteBolt11(Uuid::from_str(&filter)?)
+                        Notification::MintQuoteBolt11(QuoteId::from_str(&filter)?)
                     }
                     Kind::ProofState => Notification::ProofState(PublicKey::from_str(&filter)?),
                     Kind::Bolt12MintQuote => {
-                        Notification::MintQuoteBolt12(Uuid::from_str(&filter)?)
+                        Notification::MintQuoteBolt12(QuoteId::from_str(&filter)?)
                     }
                 };
 
@@ -70,7 +70,7 @@ impl AsRef<SubId> for IndexableParams {
 }
 
 #[cfg(feature = "mint")]
-impl Indexable for NotificationPayload<Uuid> {
+impl Indexable for NotificationPayload<QuoteId> {
     type Type = Notification;
 
     fn to_indexes(&self) -> Vec<Index<Self::Type>> {
@@ -79,13 +79,19 @@ impl Indexable for NotificationPayload<Uuid> {
                 vec![Index::from(Notification::ProofState(proof_state.y))]
             }
             NotificationPayload::MeltQuoteBolt11Response(melt_quote) => {
-                vec![Index::from(Notification::MeltQuoteBolt11(melt_quote.quote))]
+                vec![Index::from(Notification::MeltQuoteBolt11(
+                    melt_quote.quote.clone(),
+                ))]
             }
             NotificationPayload::MintQuoteBolt11Response(mint_quote) => {
-                vec![Index::from(Notification::MintQuoteBolt11(mint_quote.quote))]
+                vec![Index::from(Notification::MintQuoteBolt11(
+                    mint_quote.quote.clone(),
+                ))]
             }
             NotificationPayload::MintQuoteBolt12Response(mint_quote) => {
-                vec![Index::from(Notification::MintQuoteBolt12(mint_quote.quote))]
+                vec![Index::from(Notification::MintQuoteBolt12(
+                    mint_quote.quote.clone(),
+                ))]
             }
         }
     }

+ 4 - 4
crates/cdk-common/src/ws.rs

@@ -6,9 +6,9 @@
 use cashu::nut17::ws::JSON_RPC_VERSION;
 use cashu::nut17::{self};
 #[cfg(feature = "mint")]
-use cashu::NotificationPayload;
+use cashu::quote_id::QuoteId;
 #[cfg(feature = "mint")]
-use uuid::Uuid;
+use cashu::NotificationPayload;
 
 use crate::pub_sub::SubId;
 
@@ -48,7 +48,7 @@ pub type NotificationInner<T> = nut17::ws::NotificationInner<T, SubId>;
 #[cfg(feature = "mint")]
 /// Converts a notification with UUID identifiers to a notification with string identifiers
 pub fn notification_uuid_to_notification_string(
-    notification: NotificationInner<Uuid>,
+    notification: NotificationInner<QuoteId>,
 ) -> NotificationInner<String> {
     nut17::ws::NotificationInner {
         sub_id: notification.sub_id,
@@ -69,7 +69,7 @@ pub fn notification_uuid_to_notification_string(
 
 #[cfg(feature = "mint")]
 /// Converts a notification to a websocket message that can be sent to clients
-pub fn notification_to_ws_message(notification: NotificationInner<Uuid>) -> WsMessageOrResponse {
+pub fn notification_to_ws_message(notification: NotificationInner<QuoteId>) -> WsMessageOrResponse {
     nut17::ws::WsMessageOrResponse::Notification(nut17::ws::WsNotification {
         jsonrpc: JSON_RPC_VERSION.to_owned(),
         method: "subscribe".to_string(),

+ 1 - 0
crates/cdk-integration-tests/src/bin/start_regtest_mints.rs

@@ -260,6 +260,7 @@ fn create_ldk_settings(
             url: format!("http://127.0.0.1:{port}"),
             listen_host: "127.0.0.1".to_string(),
             listen_port: port,
+            seed: None,
             mnemonic: Some(mnemonic),
             signatory_url: None,
             signatory_certs: None,

+ 9 - 12
crates/cdk-integration-tests/src/init_pure_tests.rs

@@ -8,6 +8,7 @@ use std::{env, fs};
 use anyhow::{anyhow, bail, Result};
 use async_trait::async_trait;
 use bip39::Mnemonic;
+use cashu::quote_id::QuoteId;
 use cashu::{MeltQuoteBolt12Request, MintQuoteBolt12Request, MintQuoteBolt12Response};
 use cdk::amount::SplitTarget;
 use cdk::cdk_database::{self, MintDatabase, WalletDatabase};
@@ -80,16 +81,15 @@ impl MintConnector for DirectMintConnection {
         &self,
         quote_id: &str,
     ) -> Result<MintQuoteBolt11Response<String>, Error> {
-        let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
         self.mint
-            .check_mint_quote(&quote_id_uuid)
+            .check_mint_quote(&QuoteId::from_str(quote_id)?)
             .await
             .map(Into::into)
     }
 
     async fn post_mint(&self, request: MintRequest<String>) -> Result<MintResponse, Error> {
-        let request_uuid = request.try_into().unwrap();
-        self.mint.process_mint_request(request_uuid).await
+        let request_id: MintRequest<QuoteId> = request.try_into().unwrap();
+        self.mint.process_mint_request(request_id).await
     }
 
     async fn post_melt_quote(
@@ -106,9 +106,8 @@ impl MintConnector for DirectMintConnection {
         &self,
         quote_id: &str,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
-        let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
         self.mint
-            .check_melt_quote(&quote_id_uuid)
+            .check_melt_quote(&QuoteId::from_str(quote_id)?)
             .await
             .map(Into::into)
     }
@@ -156,7 +155,7 @@ impl MintConnector for DirectMintConnection {
         &self,
         request: MintQuoteBolt12Request,
     ) -> Result<MintQuoteBolt12Response<String>, Error> {
-        let res: MintQuoteBolt12Response<Uuid> =
+        let res: MintQuoteBolt12Response<QuoteId> =
             self.mint.get_mint_quote(request.into()).await?.try_into()?;
         Ok(res.into())
     }
@@ -165,10 +164,9 @@ impl MintConnector for DirectMintConnection {
         &self,
         quote_id: &str,
     ) -> Result<MintQuoteBolt12Response<String>, Error> {
-        let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
-        let quote: MintQuoteBolt12Response<Uuid> = self
+        let quote: MintQuoteBolt12Response<QuoteId> = self
             .mint
-            .check_mint_quote(&quote_id_uuid)
+            .check_mint_quote(&QuoteId::from_str(quote_id)?)
             .await?
             .try_into()?;
 
@@ -190,9 +188,8 @@ impl MintConnector for DirectMintConnection {
         &self,
         quote_id: &str,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
-        let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
         self.mint
-            .check_melt_quote(&quote_id_uuid)
+            .check_melt_quote(&QuoteId::from_str(quote_id)?)
             .await
             .map(Into::into)
     }

+ 3 - 0
crates/cdk-integration-tests/src/shared.rs

@@ -183,6 +183,7 @@ pub fn create_fake_wallet_settings(
             url: format!("http://127.0.0.1:{port}"),
             listen_host: "127.0.0.1".to_string(),
             listen_port: port,
+            seed: None,
             mnemonic,
             signatory_url: signatory_config.as_ref().map(|(url, _)| url.clone()),
             signatory_certs: signatory_config
@@ -233,6 +234,7 @@ pub fn create_cln_settings(
             url: format!("http://127.0.0.1:{port}"),
             listen_host: "127.0.0.1".to_string(),
             listen_port: port,
+            seed: None,
             mnemonic: Some(mnemonic),
             signatory_url: None,
             signatory_certs: None,
@@ -277,6 +279,7 @@ pub fn create_lnd_settings(
             url: format!("http://127.0.0.1:{port}"),
             listen_host: "127.0.0.1".to_string(),
             listen_port: port,
+            seed: None,
             mnemonic: Some(mnemonic),
             signatory_url: None,
             signatory_certs: None,

+ 1 - 1
crates/cdk-mint-rpc/src/proto/server.rs

@@ -677,7 +677,7 @@ impl CdkMint for MintRPCServer {
             _ => {
                 // Create a new quote with the same values
                 let quote = MintQuote::new(
-                    Some(mint_quote.id),                  // id
+                    Some(mint_quote.id.clone()),          // id
                     mint_quote.request.clone(),           // request
                     mint_quote.unit.clone(),              // unit
                     mint_quote.amount,                    // amount

+ 4 - 1
crates/cdk-mintd/src/config.rs

@@ -50,6 +50,8 @@ pub struct Info {
     pub url: String,
     pub listen_host: String,
     pub listen_port: u16,
+    /// Overrides mnemonic
+    pub seed: Option<String>,
     pub mnemonic: Option<String>,
     pub signatory_url: Option<String>,
     pub signatory_certs: Option<String>,
@@ -74,6 +76,7 @@ impl Default for Info {
             url: String::new(),
             listen_host: "127.0.0.1".to_string(),
             listen_port: 8091, // Default to port 8091 instead of 0
+            seed: None,
             mnemonic: None,
             signatory_url: None,
             signatory_certs: None,
@@ -88,7 +91,7 @@ impl Default for Info {
 impl std::fmt::Debug for Info {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         // Use a fallback approach that won't panic
-        let mnemonic_display = {
+        let mnemonic_display: String = {
             if let Some(mnemonic) = self.mnemonic.as_ref() {
                 let hash = sha256::Hash::hash(mnemonic.as_bytes());
                 format!("<hashed: {hash}>")

+ 1 - 0
crates/cdk-mintd/src/env_vars/common.rs

@@ -6,6 +6,7 @@ pub const DATABASE_URL_ENV_VAR: &str = "CDK_MINTD_DATABASE_URL"; // Legacy, main
 pub const ENV_URL: &str = "CDK_MINTD_URL";
 pub const ENV_LISTEN_HOST: &str = "CDK_MINTD_LISTEN_HOST";
 pub const ENV_LISTEN_PORT: &str = "CDK_MINTD_LISTEN_PORT";
+pub const ENV_SEED: &str = "CDK_MINTD_SEED";
 pub const ENV_MNEMONIC: &str = "CDK_MINTD_MNEMONIC";
 pub const ENV_SIGNATORY_URL: &str = "CDK_MINTD_SIGNATORY_URL";
 pub const ENV_SIGNATORY_CERTS: &str = "CDK_MINTD_SIGNATORY_CERTS";

+ 4 - 0
crates/cdk-mintd/src/env_vars/info.rs

@@ -31,6 +31,10 @@ impl Info {
             self.signatory_certs = Some(signatory_certs);
         }
 
+        if let Ok(seed) = env::var(ENV_SEED) {
+            self.seed = Some(seed);
+        }
+
         if let Ok(mnemonic) = env::var(ENV_MNEMONIC) {
             self.mnemonic = Some(mnemonic);
         }

+ 3 - 0
crates/cdk-mintd/src/lib.rs

@@ -805,6 +805,9 @@ async fn build_mint(
                 .await?,
             ))
             .await?)
+    } else if let Some(seed) = settings.info.seed.clone() {
+        let seed_bytes: Vec<u8> = seed.into();
+        Ok(mint_builder.build_with_seed(keystore, &seed_bytes).await?)
     } else if let Some(mnemonic) = settings
         .info
         .mnemonic

+ 135 - 32
crates/cdk-sql-common/src/mint/mod.rs

@@ -26,6 +26,7 @@ use cdk_common::mint::{
 };
 use cdk_common::nut00::ProofsMethods;
 use cdk_common::payment::PaymentIdentifier;
+use cdk_common::quote_id::QuoteId;
 use cdk_common::secret::Secret;
 use cdk_common::state::check_state_transition;
 use cdk_common::util::unix_time;
@@ -309,7 +310,7 @@ where
 #[inline(always)]
 async fn get_mint_quote_payments<C>(
     conn: &C,
-    quote_id: &Uuid,
+    quote_id: &QuoteId,
 ) -> Result<Vec<IncomingPayment>, Error>
 where
     C: DatabaseExecutor + Send + Sync,
@@ -327,7 +328,13 @@ where
             quote_id=:quote_id
         "#,
     )?
-    .bind("quote_id", quote_id.as_hyphenated().to_string())
+    .bind(
+        "quote_id",
+        match quote_id {
+            QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+            QuoteId::BASE64(s) => s.to_string(),
+        },
+    )
     .fetch_all(conn)
     .await?
     .into_iter()
@@ -344,7 +351,7 @@ where
 }
 
 #[inline(always)]
-async fn get_mint_quote_issuance<C>(conn: &C, quote_id: &Uuid) -> Result<Vec<Issuance>, Error>
+async fn get_mint_quote_issuance<C>(conn: &C, quote_id: &QuoteId) -> Result<Vec<Issuance>, Error>
 where
     C: DatabaseExecutor + Send + Sync,
 {
@@ -356,7 +363,13 @@ FROM mint_quote_issued
 WHERE quote_id=:quote_id
             "#,
     )?
-    .bind("quote_id", quote_id.as_hyphenated().to_string())
+    .bind(
+        "quote_id",
+        match quote_id {
+            QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+            QuoteId::BASE64(s) => s.to_string(),
+        },
+    )
     .fetch_all(conn)
     .await?
     .into_iter()
@@ -542,7 +555,7 @@ where
     #[instrument(skip(self))]
     async fn increment_mint_quote_amount_paid(
         &mut self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
         amount_paid: Amount,
         payment_id: String,
     ) -> Result<Amount, Self::Err> {
@@ -578,7 +591,13 @@ where
             FOR UPDATE
             "#,
         )?
-        .bind("quote_id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "quote_id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .fetch_one(&self.inner)
         .await
         .inspect_err(|err| {
@@ -613,7 +632,13 @@ where
             "#,
         )?
         .bind("amount_paid", new_amount_paid.to_i64())
-        .bind("quote_id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "quote_id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .execute(&self.inner)
         .await
         .inspect_err(|err| {
@@ -628,7 +653,13 @@ where
             VALUES (:quote_id, :payment_id, :amount, :timestamp)
             "#,
         )?
-        .bind("quote_id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "quote_id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .bind("payment_id", payment_id)
         .bind("amount", amount_paid.to_i64())
         .bind("timestamp", unix_time() as i64)
@@ -645,7 +676,7 @@ where
     #[instrument(skip_all)]
     async fn increment_mint_quote_amount_issued(
         &mut self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
         amount_issued: Amount,
     ) -> Result<Amount, Self::Err> {
         // Get current amount_issued from quote
@@ -657,7 +688,13 @@ where
             FOR UPDATE
             "#,
         )?
-        .bind("quote_id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "quote_id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .fetch_one(&self.inner)
         .await
         .inspect_err(|err| {
@@ -685,7 +722,13 @@ where
             "#,
         )?
         .bind("amount_issued", new_amount_issued.to_i64())
-        .bind("quote_id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "quote_id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .execute(&self.inner)
         .await
         .inspect_err(|err| {
@@ -701,7 +744,13 @@ INSERT INTO mint_quote_issued
 VALUES (:quote_id, :amount, :timestamp);
             "#,
         )?
-        .bind("quote_id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "quote_id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .bind("amount", amount_issued.to_i64())
         .bind("timestamp", current_time as i64)
         .execute(&self.inner)
@@ -741,9 +790,15 @@ VALUES (:quote_id, :amount, :timestamp);
         Ok(())
     }
 
-    async fn remove_mint_quote(&mut self, quote_id: &Uuid) -> Result<(), Self::Err> {
+    async fn remove_mint_quote(&mut self, quote_id: &QuoteId) -> Result<(), Self::Err> {
         query(r#"DELETE FROM mint_quote WHERE id=:id"#)?
-            .bind("id", quote_id.as_hyphenated().to_string())
+            .bind(
+                "id",
+                match quote_id {
+                    QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                    QuoteId::BASE64(s) => s.to_string(),
+                },
+            )
             .execute(&self.inner)
             .await?;
         Ok(())
@@ -800,13 +855,16 @@ VALUES (:quote_id, :amount, :timestamp);
 
     async fn update_melt_quote_request_lookup_id(
         &mut self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
         new_request_lookup_id: &PaymentIdentifier,
     ) -> Result<(), Self::Err> {
         query(r#"UPDATE melt_quote SET request_lookup_id = :new_req_id, request_lookup_id_kind = :new_kind WHERE id = :id"#)?
             .bind("new_req_id", new_request_lookup_id.to_string())
             .bind("new_kind",new_request_lookup_id.kind() )
-            .bind("id", quote_id.as_hyphenated().to_string())
+            .bind("id", match quote_id {
+                QuoteId::BASE64(s) => s.to_string(),
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+            })
             .execute(&self.inner)
             .await?;
         Ok(())
@@ -814,7 +872,7 @@ VALUES (:quote_id, :amount, :timestamp);
 
     async fn update_melt_quote_state(
         &mut self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
         state: MeltQuoteState,
         payment_proof: Option<String>,
     ) -> Result<(MeltQuoteState, mint::MeltQuote), Self::Err> {
@@ -842,7 +900,13 @@ VALUES (:quote_id, :amount, :timestamp);
                 AND state != :state
             "#,
         )?
-        .bind("id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .bind("state", state.to_string())
         .fetch_one(&self.inner)
         .await?
@@ -856,13 +920,22 @@ VALUES (:quote_id, :amount, :timestamp);
                 .bind("state", state.to_string())
                 .bind("paid_time", current_time as i64)
                 .bind("payment_preimage", payment_proof)
-                .bind("id", quote_id.as_hyphenated().to_string())
+                .bind("id", match quote_id {
+                    QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                    QuoteId::BASE64(s) => s.to_string(),
+                })
                 .execute(&self.inner)
                 .await
         } else {
             query(r#"UPDATE melt_quote SET state = :state WHERE id = :id"#)?
                 .bind("state", state.to_string())
-                .bind("id", quote_id.as_hyphenated().to_string())
+                .bind(
+                    "id",
+                    match quote_id {
+                        QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                        QuoteId::BASE64(s) => s.to_string(),
+                    },
+                )
                 .execute(&self.inner)
                 .await
         };
@@ -895,7 +968,7 @@ VALUES (:quote_id, :amount, :timestamp);
         Ok(())
     }
 
-    async fn get_mint_quote(&mut self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
+    async fn get_mint_quote(&mut self, quote_id: &QuoteId) -> Result<Option<MintQuote>, Self::Err> {
         let payments = get_mint_quote_payments(&self.inner, quote_id).await?;
         let issuance = get_mint_quote_issuance(&self.inner, quote_id).await?;
 
@@ -920,7 +993,13 @@ VALUES (:quote_id, :amount, :timestamp);
             FOR UPDATE
             "#,
         )?
-        .bind("id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .fetch_one(&self.inner)
         .await?
         .map(|row| sql_row_to_mint_quote(row, payments, issuance))
@@ -1053,7 +1132,7 @@ where
 {
     type Err = Error;
 
-    async fn get_mint_quote(&self, quote_id: &Uuid) -> Result<Option<MintQuote>, Self::Err> {
+    async fn get_mint_quote(&self, quote_id: &QuoteId) -> Result<Option<MintQuote>, Self::Err> {
         let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
 
         let payments = get_mint_quote_payments(&*conn, quote_id).await?;
@@ -1078,7 +1157,13 @@ where
                 mint_quote
             WHERE id = :id"#,
         )?
-        .bind("id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "id",
+            match quote_id {
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+                QuoteId::BASE64(s) => s.to_string(),
+            },
+        )
         .fetch_one(&*conn)
         .await?
         .map(|row| sql_row_to_mint_quote(row, payments, issuance))
@@ -1206,7 +1291,10 @@ where
         Ok(mint_quotes)
     }
 
-    async fn get_melt_quote(&self, quote_id: &Uuid) -> Result<Option<mint::MeltQuote>, Self::Err> {
+    async fn get_melt_quote(
+        &self,
+        quote_id: &QuoteId,
+    ) -> Result<Option<mint::MeltQuote>, Self::Err> {
         let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
         Ok(query(
             r#"
@@ -1231,7 +1319,13 @@ where
                 id=:id
             "#,
         )?
-        .bind("id", quote_id.as_hyphenated().to_string())
+        .bind(
+            "id",
+            match quote_id {
+                QuoteId::BASE64(s) => s.to_string(),
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+            },
+        )
         .fetch_one(&*conn)
         .await?
         .map(sql_row_to_melt_quote)
@@ -1386,7 +1480,7 @@ where
         &mut self,
         blinded_messages: &[PublicKey],
         blind_signatures: &[BlindSignature],
-        quote_id: Option<Uuid>,
+        quote_id: Option<QuoteId>,
     ) -> Result<(), Self::Err> {
         let current_time = unix_time();
 
@@ -1403,7 +1497,10 @@ where
             .bind("amount", u64::from(signature.amount) as i64)
             .bind("keyset_id", signature.keyset_id.to_string())
             .bind("c", signature.c.to_bytes().to_vec())
-            .bind("quote_id", quote_id.map(|q| q.hyphenated().to_string()))
+            .bind("quote_id", quote_id.as_ref().map(|q| match q {
+                QuoteId::BASE64(s) => s.to_string(),
+                QuoteId::UUID(u) => u.hyphenated().to_string(),
+            }))
             .bind(
                 "dleq_e",
                 signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex()),
@@ -1547,7 +1644,7 @@ where
     /// Get [`BlindSignature`]s for quote
     async fn get_blind_signatures_for_quote(
         &self,
-        quote_id: &Uuid,
+        quote_id: &QuoteId,
     ) -> Result<Vec<BlindSignature>, Self::Err> {
         let conn = self.pool.get().map_err(|e| Error::Database(Box::new(e)))?;
         Ok(query(
@@ -1564,7 +1661,13 @@ where
                 quote_id=:quote_id
             "#,
         )?
-        .bind("quote_id", quote_id.to_string())
+        .bind(
+            "quote_id",
+            match quote_id {
+                QuoteId::BASE64(s) => s.to_string(),
+                QuoteId::UUID(u) => u.as_hyphenated().to_string(),
+            },
+        )
         .fetch_all(&*conn)
         .await?
         .into_iter()
@@ -1658,7 +1761,7 @@ fn sql_row_to_mint_quote(
     let payment_method = column_as_string!(payment_method, PaymentMethod::from_str);
 
     Ok(MintQuote::new(
-        Some(Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?),
+        Some(QuoteId::from_str(&id)?),
         request_str,
         column_as_string!(unit, CurrencyUnit::from_str),
         amount.map(Amount::from),
@@ -1745,7 +1848,7 @@ fn sql_row_to_melt_quote(row: Vec<Column>) -> Result<mint::MeltQuote, Error> {
     };
 
     Ok(MeltQuote {
-        id: Uuid::parse_str(&id).map_err(|_| Error::InvalidUuid(id))?,
+        id: QuoteId::from_str(&id)?,
         unit: CurrencyUnit::from_str(&unit)?,
         amount: Amount::from(amount),
         request,

+ 12 - 12
crates/cdk/src/mint/issue/mod.rs

@@ -3,6 +3,7 @@ use cdk_common::payment::{
     Bolt11IncomingPaymentOptions, Bolt11Settings, Bolt12IncomingPaymentOptions,
     IncomingPaymentOptions, WaitPaymentResponse,
 };
+use cdk_common::quote_id::QuoteId;
 use cdk_common::util::unix_time;
 use cdk_common::{
     database, ensure_cdk, Amount, CurrencyUnit, Error, MintQuoteBolt11Request,
@@ -10,7 +11,6 @@ use cdk_common::{
     MintRequest, MintResponse, NotificationPayload, PaymentMethod, PublicKey,
 };
 use tracing::instrument;
-use uuid::Uuid;
 
 use crate::mint::Verification;
 use crate::Mint;
@@ -49,12 +49,12 @@ impl From<MintQuoteBolt12Request> for MintQuoteRequest {
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum MintQuoteResponse {
     /// Lightning Network BOLT11 invoice response
-    Bolt11(MintQuoteBolt11Response<Uuid>),
+    Bolt11(MintQuoteBolt11Response<QuoteId>),
     /// Lightning Network BOLT12 offer response
-    Bolt12(MintQuoteBolt12Response<Uuid>),
+    Bolt12(MintQuoteBolt12Response<QuoteId>),
 }
 
-impl TryFrom<MintQuoteResponse> for MintQuoteBolt11Response<Uuid> {
+impl TryFrom<MintQuoteResponse> for MintQuoteBolt11Response<QuoteId> {
     type Error = Error;
 
     fn try_from(response: MintQuoteResponse) -> Result<Self, Self::Error> {
@@ -65,7 +65,7 @@ impl TryFrom<MintQuoteResponse> for MintQuoteBolt11Response<Uuid> {
     }
 }
 
-impl TryFrom<MintQuoteResponse> for MintQuoteBolt12Response<Uuid> {
+impl TryFrom<MintQuoteResponse> for MintQuoteBolt12Response<QuoteId> {
     type Error = Error;
 
     fn try_from(response: MintQuoteResponse) -> Result<Self, Self::Error> {
@@ -82,7 +82,7 @@ impl TryFrom<MintQuote> for MintQuoteResponse {
     fn try_from(quote: MintQuote) -> Result<Self, Self::Error> {
         match quote.payment_method {
             PaymentMethod::Bolt11 => {
-                let bolt11_response: MintQuoteBolt11Response<Uuid> = quote.into();
+                let bolt11_response: MintQuoteBolt11Response<QuoteId> = quote.into();
                 Ok(MintQuoteResponse::Bolt11(bolt11_response))
             }
             PaymentMethod::Bolt12 => {
@@ -298,12 +298,12 @@ impl Mint {
 
         match payment_method {
             PaymentMethod::Bolt11 => {
-                let res: MintQuoteBolt11Response<Uuid> = quote.clone().into();
+                let res: MintQuoteBolt11Response<QuoteId> = quote.clone().into();
                 self.pubsub_manager
                     .broadcast(NotificationPayload::MintQuoteBolt11Response(res));
             }
             PaymentMethod::Bolt12 => {
-                let res: MintQuoteBolt12Response<Uuid> = quote.clone().try_into()?;
+                let res: MintQuoteBolt12Response<QuoteId> = quote.clone().try_into()?;
                 self.pubsub_manager
                     .broadcast(NotificationPayload::MintQuoteBolt12Response(res));
             }
@@ -333,7 +333,7 @@ impl Mint {
     /// * `Ok(())` if removal was successful
     /// * `Error` if the quote doesn't exist or removal fails
     #[instrument(skip_all)]
-    pub async fn remove_mint_quote(&self, quote_id: &Uuid) -> Result<(), Error> {
+    pub async fn remove_mint_quote(&self, quote_id: &QuoteId) -> Result<(), Error> {
         let mut tx = self.localstore.begin_transaction().await?;
         tx.remove_mint_quote(quote_id).await?;
         tx.commit().await?;
@@ -421,7 +421,7 @@ impl Mint {
     /// * `MintQuoteResponse` - The current state of the quote
     /// * `Error` if the quote doesn't exist or checking fails
     #[instrument(skip(self))]
-    pub async fn check_mint_quote(&self, quote_id: &Uuid) -> Result<MintQuoteResponse, Error> {
+    pub async fn check_mint_quote(&self, quote_id: &QuoteId) -> Result<MintQuoteResponse, Error> {
         let mut quote = self
             .localstore
             .get_mint_quote(quote_id)
@@ -454,7 +454,7 @@ impl Mint {
     #[instrument(skip_all)]
     pub async fn process_mint_request(
         &self,
-        mint_request: MintRequest<Uuid>,
+        mint_request: MintRequest<QuoteId>,
     ) -> Result<MintResponse, Error> {
         let mut mint_quote = self
             .localstore
@@ -563,7 +563,7 @@ impl Mint {
                 .map(|p| p.blinded_secret)
                 .collect::<Vec<PublicKey>>(),
             &blind_signatures,
-            Some(mint_request.quote),
+            Some(mint_request.quote.clone()),
         )
         .await?;
 

+ 13 - 13
crates/cdk/src/mint/melt.rs

@@ -11,10 +11,10 @@ use cdk_common::payment::{
     Bolt11OutgoingPaymentOptions, Bolt12OutgoingPaymentOptions, OutgoingPaymentOptions,
     PaymentIdentifier,
 };
+use cdk_common::quote_id::QuoteId;
 use cdk_common::{MeltOptions, MeltQuoteBolt12Request};
 use lightning::offers::offer::Offer;
 use tracing::instrument;
-use uuid::Uuid;
 
 use super::{
     CurrencyUnit, MeltQuote, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MeltRequest, Mint,
@@ -114,7 +114,7 @@ impl Mint {
     pub async fn get_melt_quote(
         &self,
         melt_quote_request: MeltQuoteRequest,
-    ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
+    ) -> Result<MeltQuoteBolt11Response<QuoteId>, Error> {
         match melt_quote_request {
             MeltQuoteRequest::Bolt11(bolt11_request) => {
                 self.get_melt_bolt11_quote_impl(&bolt11_request).await
@@ -130,7 +130,7 @@ impl Mint {
     async fn get_melt_bolt11_quote_impl(
         &self,
         melt_request: &MeltQuoteBolt11Request,
-    ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
+    ) -> Result<MeltQuoteBolt11Response<QuoteId>, Error> {
         let MeltQuoteBolt11Request {
             request,
             unit,
@@ -222,7 +222,7 @@ impl Mint {
     async fn get_melt_bolt12_quote_impl(
         &self,
         melt_request: &MeltQuoteBolt12Request,
-    ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
+    ) -> Result<MeltQuoteBolt11Response<QuoteId>, Error> {
         let MeltQuoteBolt12Request {
             request,
             unit,
@@ -322,8 +322,8 @@ impl Mint {
     #[instrument(skip(self))]
     pub async fn check_melt_quote(
         &self,
-        quote_id: &Uuid,
-    ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
+        quote_id: &QuoteId,
+    ) -> Result<MeltQuoteBolt11Response<QuoteId>, Error> {
         let quote = self
             .localstore
             .get_melt_quote(quote_id)
@@ -363,7 +363,7 @@ impl Mint {
     pub async fn check_melt_expected_ln_fees(
         &self,
         melt_quote: &MeltQuote,
-        melt_request: &MeltRequest<Uuid>,
+        melt_request: &MeltRequest<QuoteId>,
     ) -> Result<Option<Amount>, Error> {
         let quote_msats = to_unit(melt_quote.amount, &melt_quote.unit, &CurrencyUnit::Msat)
             .expect("Quote unit is checked above that it can convert to msat");
@@ -445,7 +445,7 @@ impl Mint {
         &self,
         tx: &mut Box<dyn MintTransaction<'_, database::Error> + Send + Sync + '_>,
         input_verification: Verification,
-        melt_request: &MeltRequest<Uuid>,
+        melt_request: &MeltRequest<QuoteId>,
     ) -> Result<(ProofWriter, MeltQuote), Error> {
         let (state, quote) = tx
             .update_melt_quote_state(melt_request.quote(), MeltQuoteState::Pending, None)
@@ -518,8 +518,8 @@ impl Mint {
     #[instrument(skip_all)]
     pub async fn melt(
         &self,
-        melt_request: &MeltRequest<Uuid>,
-    ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
+        melt_request: &MeltRequest<QuoteId>,
+    ) -> Result<MeltQuoteBolt11Response<QuoteId>, Error> {
         use std::sync::Arc;
         async fn check_payment_state(
             ln: Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>,
@@ -743,10 +743,10 @@ impl Mint {
         mut tx: Box<dyn MintTransaction<'_, database::Error> + Send + Sync + '_>,
         mut proof_writer: ProofWriter,
         quote: MeltQuote,
-        melt_request: &MeltRequest<Uuid>,
+        melt_request: &MeltRequest<QuoteId>,
         payment_preimage: Option<String>,
         total_spent: Amount,
-    ) -> Result<MeltQuoteBolt11Response<Uuid>, Error> {
+    ) -> Result<MeltQuoteBolt11Response<QuoteId>, Error> {
         let input_ys = melt_request.inputs().ys()?;
 
         proof_writer
@@ -823,7 +823,7 @@ impl Mint {
                         .map(|o| o.blinded_secret)
                         .collect::<Vec<PublicKey>>(),
                     &change_sigs,
-                    Some(quote.id),
+                    Some(quote.id.clone()),
                 )
                 .await?;
 

+ 2 - 2
crates/cdk/src/mint/mod.rs

@@ -12,6 +12,7 @@ use cdk_common::database::MintAuthDatabase;
 use cdk_common::database::{self, MintDatabase, MintTransaction};
 use cdk_common::nuts::{self, BlindSignature, BlindedMessage, CurrencyUnit, Id, Kind};
 use cdk_common::payment::WaitPaymentResponse;
+pub use cdk_common::quote_id::QuoteId;
 use cdk_common::secret;
 use cdk_signatory::signatory::{Signatory, SignatoryKeySet};
 use futures::StreamExt;
@@ -21,7 +22,6 @@ use subscription::PubSubManager;
 use tokio::sync::{Mutex, Notify};
 use tokio::task::{JoinHandle, JoinSet};
 use tracing::instrument;
-use uuid::Uuid;
 
 use crate::cdk_payment::{self, MintPayment};
 use crate::error::Error;
@@ -758,7 +758,7 @@ impl Mint {
         &self,
         tx: &mut Box<dyn MintTransaction<'_, cdk_database::Error> + Send + Sync + '_>,
         melt_quote: &MeltQuote,
-        melt_request: &MeltRequest<Uuid>,
+        melt_request: &MeltRequest<QuoteId>,
     ) -> Result<Option<Amount>, Error> {
         let mint_quote = match tx
             .get_mint_quote_by_request(&melt_quote.request.to_string())

+ 8 - 6
crates/cdk/src/mint/subscription/manager.rs

@@ -5,8 +5,8 @@ use std::sync::Arc;
 use cdk_common::database::{self, MintDatabase};
 use cdk_common::mint::MintQuote;
 use cdk_common::nut17::Notification;
+use cdk_common::quote_id::QuoteId;
 use cdk_common::{Amount, MintQuoteBolt12Response, NotificationPayload, PaymentMethod};
-use uuid::Uuid;
 
 use super::OnSubscription;
 use crate::nuts::{
@@ -20,7 +20,9 @@ use crate::pub_sub;
 ///
 /// 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<NotificationPayload<Uuid>, Notification, OnSubscription>);
+pub struct PubSubManager(
+    pub_sub::Manager<NotificationPayload<QuoteId>, Notification, OnSubscription>,
+);
 
 #[allow(clippy::default_constructed_unit_structs)]
 impl Default for PubSubManager {
@@ -36,7 +38,7 @@ impl From<Arc<dyn MintDatabase<database::Error> + Send + Sync>> for PubSubManage
 }
 
 impl Deref for PubSubManager {
-    type Target = pub_sub::Manager<NotificationPayload<Uuid>, Notification, OnSubscription>;
+    type Target = pub_sub::Manager<NotificationPayload<QuoteId>, Notification, OnSubscription>;
 
     fn deref(&self) -> &Self::Target {
         &self.0
@@ -88,7 +90,7 @@ impl PubSubManager {
     }
 
     /// Helper function to emit a MintQuoteBolt11Response status
-    pub fn mint_quote_bolt11_status<E: Into<MintQuoteBolt11Response<Uuid>>>(
+    pub fn mint_quote_bolt11_status<E: Into<MintQuoteBolt11Response<QuoteId>>>(
         &self,
         quote: E,
         new_state: MintQuoteState,
@@ -100,7 +102,7 @@ impl PubSubManager {
     }
 
     /// Helper function to emit a MintQuoteBolt11Response status
-    pub fn mint_quote_bolt12_status<E: TryInto<MintQuoteBolt12Response<Uuid>>>(
+    pub fn mint_quote_bolt12_status<E: TryInto<MintQuoteBolt12Response<QuoteId>>>(
         &self,
         quote: E,
         amount_paid: Amount,
@@ -117,7 +119,7 @@ impl PubSubManager {
     }
 
     /// Helper function to emit a MeltQuoteBolt11Response status
-    pub fn melt_quote_status<E: Into<MeltQuoteBolt11Response<Uuid>>>(
+    pub fn melt_quote_status<E: Into<MeltQuoteBolt11Response<QuoteId>>>(
         &self,
         quote: E,
         payment_preimage: Option<String>,

+ 6 - 5
crates/cdk/src/mint/subscription/on_subscription.rs

@@ -6,8 +6,8 @@ use std::sync::Arc;
 use cdk_common::database::{self, MintDatabase};
 use cdk_common::nut17::Notification;
 use cdk_common::pub_sub::OnNewSubscription;
+use cdk_common::quote_id::QuoteId;
 use cdk_common::{MintQuoteBolt12Response, NotificationPayload, PaymentMethod};
-use uuid::Uuid;
 
 use crate::nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey};
 
@@ -21,7 +21,7 @@ pub struct OnSubscription(pub(crate) Option<Arc<dyn MintDatabase<database::Error
 
 #[async_trait::async_trait]
 impl OnNewSubscription for OnSubscription {
-    type Event = NotificationPayload<Uuid>;
+    type Event = NotificationPayload<QuoteId>;
     type Index = Notification;
 
     async fn on_new_subscription(
@@ -65,7 +65,7 @@ impl OnNewSubscription for OnSubscription {
                         quotes
                             .into_iter()
                             .filter_map(|quote| quote.map(|x| x.into()))
-                            .map(|x: MeltQuoteBolt11Response<Uuid>| x.into())
+                            .map(|x: MeltQuoteBolt11Response<QuoteId>| x.into())
                             .collect::<Vec<_>>()
                     })
                     .map_err(|e| e.to_string())?,
@@ -82,12 +82,13 @@ impl OnNewSubscription for OnSubscription {
                             .filter_map(|quote| {
                                 quote.and_then(|x| match x.payment_method {
                                     PaymentMethod::Bolt11 => {
-                                        let response: MintQuoteBolt11Response<Uuid> = x.into();
+                                        let response: MintQuoteBolt11Response<QuoteId> = x.into();
                                         Some(response.into())
                                     }
                                     PaymentMethod::Bolt12 => match x.try_into() {
                                         Ok(response) => {
-                                            let response: MintQuoteBolt12Response<Uuid> = response;
+                                            let response: MintQuoteBolt12Response<QuoteId> =
+                                                response;
                                             Some(response.into())
                                         }
                                         Err(_) => None,