Explorar o código

One key manager per mint

Cesar Rodas hai 3 semanas
pai
achega
866e0533dc

+ 1 - 0
crates/cdk/Cargo.toml

@@ -40,6 +40,7 @@ cdk-common.workspace = true
 cbor-diag.workspace = true
 async-trait.workspace = true
 anyhow.workspace = true
+once_cell = "1.20.2"
 bitcoin.workspace = true
 ciborium.workspace = true
 lightning.workspace = true

+ 3 - 3
crates/cdk/src/wallet/auth/auth_wallet.rs

@@ -180,7 +180,7 @@ impl AuthWallet {
     pub async fn load_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> {
         Ok((*self
             .key_manager
-            .get_keys(&self.mint_url, &keyset_id)
+            .get_keys(&keyset_id)
             .await?)
             .clone())
     }
@@ -193,7 +193,7 @@ impl AuthWallet {
     /// but will fall back to online if needed.
     #[instrument(skip(self))]
     pub async fn load_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
-        if let Ok(keysets) = self.key_manager.get_keysets(&self.mint_url).await {
+        if let Ok(keysets) = self.key_manager.get_keysets().await {
             let auth_keysets: Vec<KeySetInfo> = keysets
                 .into_iter()
                 .filter(|k| k.unit == CurrencyUnit::Auth)
@@ -218,7 +218,7 @@ impl AuthWallet {
 
         let auth_keysets = self
             .key_manager
-            .refresh(&self.mint_url)
+            .refresh()
             .await?
             .into_iter()
             .filter(|k| k.unit == CurrencyUnit::Auth && k.active)

+ 16 - 7
crates/cdk/src/wallet/builder.rs

@@ -133,11 +133,21 @@ impl WalletBuilder {
     /// Set auth CAT (Clear Auth Token)
     #[cfg(feature = "auth")]
     pub fn set_auth_cat(mut self, cat: String) -> Self {
-        let key_manager = self.key_manager.clone().unwrap_or_else(KeyManager::new);
+        let mint_url = self.mint_url.clone().expect("Mint URL required");
+        let localstore = self.localstore.clone().expect("Localstore required");
+
+        let key_manager = self.key_manager.clone().unwrap_or_else(|| {
+            KeyManager::new(
+                mint_url.clone(),
+                localstore.clone(),
+                Arc::new(HttpClient::new(mint_url.clone(), None)),
+            )
+        });
+
         self.auth_wallet = Some(AuthWallet::new(
-            self.mint_url.clone().expect("Mint URL required"),
+            mint_url,
             Some(AuthToken::ClearAuth(cat)),
-            self.localstore.clone().expect("Localstore required"),
+            localstore,
             key_manager,
             HashMap::new(),
             None,
@@ -177,16 +187,15 @@ impl WalletBuilder {
             }
         };
 
-        let key_manager = self.key_manager.unwrap_or_else(KeyManager::new);
-        let key_sub_id =
-            key_manager.register_mint(mint_url.clone(), localstore.clone(), client.clone());
+        let key_manager = self.key_manager.unwrap_or_else(|| {
+            KeyManager::new(mint_url.clone(), localstore.clone(), client.clone())
+        });
 
         Ok(Wallet {
             mint_url,
             unit,
             localstore,
             key_manager,
-            _key_sub_id: key_sub_id,
             target_proof_count: self.target_proof_count.unwrap_or(3),
             #[cfg(feature = "auth")]
             auth_wallet: Arc::new(RwLock::new(self.auth_wallet)),

+ 143 - 242
crates/cdk/src/wallet/key_manager/mod.rs

@@ -20,7 +20,6 @@
 //! ```
 
 use std::collections::HashMap;
-use std::sync::atomic::AtomicUsize;
 use std::sync::Arc;
 use std::time::{Duration, Instant};
 
@@ -31,9 +30,9 @@ use cdk_common::nuts::{KeySetInfo, Keys};
 use cdk_common::parking_lot::{Mutex as ParkingLotMutex, RwLock as ParkingLotRwLock};
 use cdk_common::task::spawn;
 use cdk_common::MintInfo;
-use tokio::sync::{mpsc, Semaphore};
+use once_cell::sync::Lazy;
+use tokio::sync::mpsc;
 use tokio::task::JoinHandle;
-use tracing::{debug, error};
 use worker::MessageToWorker;
 
 mod scheduler;
@@ -50,9 +49,6 @@ use crate::Error;
 /// Refresh interval for background key refresh (5 minutes)
 const DEFAULT_REFRESH_INTERVAL: Duration = Duration::from_secs(300);
 
-/// Maximum concurrent HTTP requests to mint servers
-const MAX_CONCURRENT_HTTP_REQUESTS: usize = 5;
-
 const MAX_RETRY: usize = 50;
 const RETRY_SLEEP: Duration = Duration::from_millis(100);
 
@@ -98,234 +94,177 @@ impl MintKeyCache {
     }
 }
 
-/// External resources needed to manage keys for a mint
+/// Shared worker state for a mint
 ///
-/// Combines storage and client into a single struct to keep them paired together.
-#[derive(Clone)]
-pub(super) struct MintResources {
-    /// Storage backend for persistence
-    storage: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
+/// One worker per mint_url, shared across all KeyManager instances for that mint.
+#[allow(dead_code)]
+struct SharedWorker {
+    /// Mint URL
+    mint_url: MintUrl,
+
+    /// Client for HTTP requests (shared across all instances)
+    client: Arc<dyn MintConnector + Send + Sync>,
 
     #[cfg(feature = "auth")]
     auth_client: Arc<dyn AuthMintConnector + Send + Sync>,
 
-    /// Client for fetching keys from mint
-    client: Arc<dyn MintConnector + Send + Sync>,
-}
+    /// All storages registered for this mint
+    storages:
+        Arc<ParkingLotRwLock<Vec<Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>>>>,
 
-/// Per-mint registration info
-///
-/// Contains all resources and cached data for managing keys for a single mint.
-#[derive(Clone)]
-pub(super) struct MintRegistration {
-    /// Mint URL
-    pub mint_url: MintUrl,
+    /// Shared cache
+    cache: Arc<ArcSwap<MintKeyCache>>,
 
-    /// External resources (storage + client)
-    pub resources: Arc<ParkingLotRwLock<HashMap<usize, MintResources>>>,
+    /// Message sender to worker
+    tx: mpsc::UnboundedSender<MessageToWorker>,
 
-    /// Cached data
-    pub cache: Arc<ArcSwap<MintKeyCache>>,
+    /// Worker task handle
+    task: Option<JoinHandle<()>>,
 }
 
-impl std::fmt::Debug for MintRegistration {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("MintRegistration")
-            .field("mint_url", &self.mint_url)
-            .field("resources", &"<MintResources>")
-            .field("cache", &self.cache)
-            .finish()
+impl Drop for SharedWorker {
+    fn drop(&mut self) {
+        tracing::debug!("Dropping SharedWorker for {}", self.mint_url);
+        let _ = self.tx.send(MessageToWorker::Stop);
+        if let Some(task) = self.task.take() {
+            task.abort();
+        }
     }
 }
 
-pub(super) type Mints = Arc<ParkingLotRwLock<HashMap<MintUrl, MintRegistration>>>;
+/// Global registry of workers, one per mint_url
+static WORKER_REGISTRY: Lazy<Arc<ParkingLotRwLock<HashMap<MintUrl, Arc<SharedWorker>>>>> =
+    Lazy::new(|| Arc::new(ParkingLotRwLock::new(HashMap::new())));
 
-/// Global key manager shared across all wallets
-///
-/// Centralizes key management with in-memory caching and background refresh.
-/// All wallets share the same cache for each mint, avoiding duplicate fetches.
+/// Key manager for a single mint
 ///
-/// Spawns a background task on creation that refreshes keys every 5 minutes.
-/// Dropping the KeyManager stops the background task.
+/// Manages keys for a specific mint with in-memory caching and background refresh.
+/// Multiple KeyManager instances for the same mint share a single worker.
 pub struct KeyManager {
-    /// Registered mints by URL (using parking_lot for sync access)
-    mints: Mints,
-
-    /// Message sender to refresh task
-    tx: mpsc::UnboundedSender<MessageToWorker>,
-
-    /// Background refresh task handle
-    refresh_task: Arc<ParkingLotMutex<Option<JoinHandle<()>>>>,
-
-    /// Refresh interval
-    refresh_interval: Duration,
-
-    /// Internal counter for each registered/mint wallet
-    counter: AtomicUsize,
+    /// Mint URL
+    mint_url: MintUrl,
 
-    /// Semaphore to limit concurrent HTTP requests to mint servers
-    /// This is stored to keep it alive and is passed to the refresh loop
-    #[allow(dead_code)]
-    refresh_semaphore: Arc<Semaphore>,
+    /// Reference to shared worker
+    worker: Arc<SharedWorker>,
 }
 
 impl std::fmt::Debug for KeyManager {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("KeyManager")
-            .field("mints", &self.mints)
-            .field("refresh_interval", &self.refresh_interval)
-            .finish()
-    }
-}
-
-/// KeySubscription
-pub struct KeySubscription {
-    /// Registered mints by URL (using parking_lot for sync access)
-    mints: Mints,
-    mint_url: MintUrl,
-    id: usize,
-}
-
-impl std::fmt::Debug for KeySubscription {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("KeyManager")
             .field("mint_url", &self.mint_url)
             .finish()
     }
 }
 
-impl Drop for KeySubscription {
-    fn drop(&mut self) {
-        KeyManager::deregister_mint(self.mints.clone(), self.mint_url.clone(), self.id);
-    }
-}
-
 impl KeyManager {
-    /// Create a new KeyManager with default 5-minute refresh interval
+    /// Create a new KeyManager for a specific mint with default 5-minute refresh interval
     ///
-    /// Spawns a background task that refreshes all registered mints periodically.
-    pub fn new() -> Arc<Self> {
-        Self::with_refresh_interval(DEFAULT_REFRESH_INTERVAL)
+    /// If a worker for this mint already exists, reuses it. Otherwise creates a new one.
+    pub fn new(
+        mint_url: MintUrl,
+        storage: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
+        client: Arc<dyn MintConnector + Send + Sync>,
+    ) -> Arc<Self> {
+        Self::with_refresh_interval(mint_url, storage, client, DEFAULT_REFRESH_INTERVAL)
     }
 
     /// Create a new KeyManager with custom refresh interval
-    pub fn with_refresh_interval(refresh_interval: Duration) -> Arc<Self> {
-        let (tx, rx) = mpsc::unbounded_channel();
-        let refresh_semaphore = Arc::new(Semaphore::new(MAX_CONCURRENT_HTTP_REQUESTS));
-
-        let manager = Self {
-            mints: Arc::new(ParkingLotRwLock::new(HashMap::new())),
-            tx,
-            refresh_task: Arc::new(ParkingLotMutex::new(None)),
-            refresh_interval,
-            refresh_semaphore: refresh_semaphore.clone(),
-            counter: 0.into(),
+    pub fn with_refresh_interval(
+        mint_url: MintUrl,
+        storage: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
+        client: Arc<dyn MintConnector + Send + Sync>,
+        refresh_interval: Duration,
+    ) -> Arc<Self> {
+        tracing::debug!("Creating KeyManager for mint: {}", mint_url);
+
+        let worker = {
+            let existing_worker = { WORKER_REGISTRY.read().get(&mint_url).cloned() };
+
+            if let Some(existing_worker) = existing_worker {
+                // Reuse existing worker
+                tracing::debug!("Reusing existing worker for {}", mint_url);
+                existing_worker.storages.write().push(storage.clone());
+                existing_worker.clone()
+            } else {
+                let mut registry = WORKER_REGISTRY.write();
+
+                // Create new worker
+                tracing::debug!("Creating new worker for {}", mint_url);
+
+                registry
+                    .entry(mint_url.clone())
+                    .or_insert_with(|| {
+                        let (tx, rx) = mpsc::unbounded_channel();
+                        let cache = Arc::new(ArcSwap::from_pointee(MintKeyCache::empty()));
+
+                        #[cfg(feature = "auth")]
+                        let auth_client = Arc::new(AuthHttpClient::new(mint_url.clone(), None));
+
+                        let storages = Arc::new(ParkingLotRwLock::new(vec![storage.clone()]));
+
+                        let task = {
+                            let mint_url = mint_url.clone();
+                            let client = client.clone();
+                            #[cfg(feature = "auth")]
+                            let auth_client = auth_client.clone();
+                            let storages = storages.clone();
+                            let cache = cache.clone();
+
+                            spawn(Self::refresh_loop(
+                                rx,
+                                mint_url,
+                                client,
+                                #[cfg(feature = "auth")]
+                                auth_client,
+                                storages,
+                                cache,
+                                refresh_interval,
+                            ))
+                        };
+
+                        let worker = Arc::new(SharedWorker {
+                            mint_url: mint_url.clone(),
+                            client,
+                            #[cfg(feature = "auth")]
+                            auth_client,
+                            storages,
+                            cache,
+                            tx: tx.clone(),
+                            task: Some(task),
+                        });
+
+                        // Trigger initial sync
+                        let _ = tx.send(MessageToWorker::SyncMint);
+
+                        worker
+                    })
+                    .clone()
+            }
         };
 
-        let mints = manager.mints.clone();
-        let refresh_interval = manager.refresh_interval;
-        let task = spawn(async move {
-            Self::refresh_loop(rx, mints, refresh_interval, refresh_semaphore).await;
-        });
+        let manager = Arc::new(Self { mint_url, worker });
 
-        {
-            let mut refresh_task = manager.refresh_task.lock();
-            *refresh_task = Some(task);
-        }
+        tracing::debug!("KeyManager created/attached for mint: {}", manager.mint_url);
 
-        Arc::new(manager)
+        manager
     }
 
     /// Send a message to the background refresh task
     fn send_message(&self, msg: MessageToWorker) {
         let _ = self
+            .worker
             .tx
             .send(msg)
-            .inspect_err(|e| error!("Failed to send message to refresh task: {}", e));
-    }
-
-    /// Register a mint for key management
-    ///
-    /// Registers the mint immediately and triggers an initial key fetch in the background.
-    /// The mint will be automatically refreshed every `refresh_interval`.
-    pub fn register_mint(
-        &self,
-        mint_url: MintUrl,
-        storage: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
-        client: Arc<dyn MintConnector + Send + Sync>,
-    ) -> Arc<KeySubscription> {
-        debug!("Registering mint: {}", mint_url);
-        let mut mints = self.mints.write();
-
-        let mint = mints.entry(mint_url.clone()).or_insert_with(|| {
-            let cache = Arc::new(ArcSwap::from_pointee(MintKeyCache::empty()));
-
-            MintRegistration {
-                mint_url: mint_url.clone(),
-                resources: Arc::new(ParkingLotRwLock::new(HashMap::new())),
-                cache,
-            }
-        });
-
-        let id = self
-            .counter
-            .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
-
-        #[cfg(feature = "auth")]
-        let mint_resource = MintResources {
-            storage,
-            client,
-            auth_client: Arc::new(AuthHttpClient::new(mint_url.clone(), None)),
-        };
-
-        #[cfg(not(feature = "auth"))]
-        let mint_resource = MintResources { storage, client };
-
-        mint.resources.write().insert(id, mint_resource);
-
-        drop(mints);
-
-        debug!("Mint registered: {}", mint_url);
-
-        self.send_message(MessageToWorker::SyncMint(mint_url.clone()));
-
-        Arc::new(KeySubscription {
-            mints: self.mints.clone(),
-            mint_url,
-            id,
-        })
-    }
-
-    fn deregister_mint(locked_mints: Mints, mint_url: MintUrl, internal_id: usize) {
-        let mut mints = locked_mints.write();
-        let mint = if let Some(r) = mints.remove(&mint_url) {
-            r
-        } else {
-            return;
-        };
-        let mut r = mint.resources.write();
-        r.remove(&internal_id);
-
-        if !r.is_empty() {
-            // add mint back, as there are other wallets to the same mint_url active
-            drop(r);
-            mints.insert(mint_url, mint);
-        }
+            .inspect_err(|e| tracing::error!("Failed to send message to refresh task: {}", e));
     }
 
     /// Get keys for a keyset (cache-first with automatic refresh)
     ///
     /// Returns keys from cache if available. If not cached, triggers a refresh
-    /// and waits up to 2 seconds for the keys to arrive.
-    pub async fn get_keys(&self, mint_url: &MintUrl, keyset_id: &Id) -> Result<Arc<Keys>, Error> {
-        let shared_cache = {
-            let mints = self.mints.read();
-            let registration = mints.get(mint_url).ok_or(Error::IncorrectMint)?;
-            registration.cache.clone()
-        };
-
+    /// and waits up to 5 seconds for the keys to arrive.
+    pub async fn get_keys(&self, keyset_id: &Id) -> Result<Arc<Keys>, Error> {
         for _ in 0..MAX_RETRY {
-            let cache = shared_cache.load();
+            let cache = self.worker.cache.load();
             if cache.is_ready {
                 return cache
                     .keys_by_id
@@ -333,7 +272,7 @@ impl KeyManager {
                     .cloned()
                     .ok_or(Error::UnknownKeySet);
             }
-            self.send_message(MessageToWorker::SyncMint(mint_url.to_owned()));
+            self.send_message(MessageToWorker::SyncMint);
             tokio::time::sleep(RETRY_SLEEP).await;
         }
 
@@ -341,19 +280,9 @@ impl KeyManager {
     }
 
     /// Get keyset info by ID (cache-first with automatic refresh)
-    pub async fn get_keyset_by_id(
-        &self,
-        mint_url: &MintUrl,
-        keyset_id: &Id,
-    ) -> Result<Arc<KeySetInfo>, Error> {
-        let shared_cache = {
-            let mints = self.mints.read();
-            let registration = mints.get(mint_url).ok_or(Error::IncorrectMint)?;
-            registration.cache.clone()
-        };
-
+    pub async fn get_keyset_by_id(&self, keyset_id: &Id) -> Result<Arc<KeySetInfo>, Error> {
         for _ in 0..MAX_RETRY {
-            let cache = shared_cache.load();
+            let cache = self.worker.cache.load();
             if cache.is_ready {
                 return cache
                     .keysets_by_id
@@ -361,20 +290,16 @@ impl KeyManager {
                     .cloned()
                     .ok_or(Error::UnknownKeySet);
             }
-            self.send_message(MessageToWorker::SyncMint(mint_url.to_owned()));
+            self.send_message(MessageToWorker::SyncMint);
             tokio::time::sleep(RETRY_SLEEP).await;
         }
 
         Err(Error::UnknownKeySet)
     }
 
-    /// Get all keysets for a mint (cache-first with automatic refresh)
-    pub async fn get_keysets(&self, mint_url: &MintUrl) -> Result<Vec<KeySetInfo>, Error> {
-        let shared_cache = {
-            let mints = self.mints.read();
-            let registration = mints.get(mint_url).ok_or(Error::IncorrectMint)?;
-            registration.cache.clone()
-        };
+    /// Get all keysets for the mint (cache-first with automatic refresh)
+    pub async fn get_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
+        let shared_cache = self.worker.cache.clone();
 
         for _ in 0..MAX_RETRY {
             let cache = shared_cache.load();
@@ -391,30 +316,22 @@ impl KeyManager {
                 };
             }
 
-            self.send_message(MessageToWorker::SyncMint(mint_url.to_owned()));
+            self.send_message(MessageToWorker::SyncMint);
             tokio::time::sleep(RETRY_SLEEP).await;
         }
 
         Err(Error::UnknownKeySet)
     }
 
-    /// Get all active keysets for a mint (cache-only, no refresh)
-    pub async fn get_active_keysets(
-        &self,
-        mint_url: &MintUrl,
-    ) -> Result<Vec<Arc<KeySetInfo>>, Error> {
-        let shared_cache = {
-            let mints = self.mints.read();
-            let registration = mints.get(mint_url).ok_or(Error::IncorrectMint)?;
-            registration.cache.clone()
-        };
-
+    /// Get all active keysets for the mint (cache-first with automatic refresh)
+    pub async fn get_active_keysets(&self) -> Result<Vec<Arc<KeySetInfo>>, Error> {
+        let shared_cache = self.worker.cache.clone();
         for _ in 0..MAX_RETRY {
             let cache = shared_cache.load();
             if cache.is_ready {
                 return Ok(cache.active_keysets.clone());
             }
-            self.send_message(MessageToWorker::SyncMint(mint_url.to_owned()));
+            self.send_message(MessageToWorker::SyncMint);
             tokio::time::sleep(RETRY_SLEEP).await;
         }
 
@@ -423,18 +340,13 @@ impl KeyManager {
 
     /// Trigger a refresh and wait for it to complete
     ///
-    /// Sends a refresh message to the background task and waits up to 2 seconds
+    /// Sends a refresh message to the background task and waits up to 5 seconds
     /// for the cache to be updated with a newer version.
-    pub async fn refresh(&self, mint_url: &MintUrl) -> Result<Vec<KeySetInfo>, Error> {
-        let shared_cache = {
-            let mints = self.mints.read();
-            let registration = mints.get(mint_url).ok_or(Error::IncorrectMint)?;
-            registration.cache.clone()
-        };
-
+    pub async fn refresh(&self) -> Result<Vec<KeySetInfo>, Error> {
+        let shared_cache = self.worker.cache.clone();
         let last_version = shared_cache.load().refresh_version;
 
-        self.send_message(MessageToWorker::FetchMint(mint_url.clone()));
+        self.send_message(MessageToWorker::FetchMint);
 
         for _ in 0..MAX_RETRY {
             if let Some(keysets) = {
@@ -460,19 +372,8 @@ impl KeyManager {
         Err(Error::UnknownKeySet)
     }
 
-    /// Trigger a refresh for a specific mint (non-blocking)
-    pub fn refresh_now(&self, mint_url: &MintUrl) {
-        self.send_message(MessageToWorker::FetchMint(mint_url.clone()));
-    }
-}
-
-impl Drop for KeyManager {
-    fn drop(&mut self) {
-        self.send_message(MessageToWorker::Stop);
-        if let Some(mut task) = self.refresh_task.try_lock() {
-            if let Some(handle) = task.take() {
-                handle.abort();
-            }
-        }
+    /// Trigger a refresh (non-blocking)
+    pub fn refresh_now(&self) {
+        self.send_message(MessageToWorker::FetchMint);
     }
 }

+ 218 - 232
crates/cdk/src/wallet/key_manager/worker.rs

@@ -3,19 +3,23 @@
 use std::sync::Arc;
 use std::time::Duration;
 
+use arc_swap::ArcSwap;
 use cdk_common::database::WalletDatabase;
 use cdk_common::mint_url::MintUrl;
+use cdk_common::parking_lot::RwLock as ParkingLotRwLock;
 use cdk_common::task::spawn;
 use cdk_common::util::unix_time;
 use cdk_common::KeySet;
-use tokio::sync::Semaphore;
-use tracing::{debug, error, warn};
 
 use super::scheduler::RefreshScheduler;
-use super::{KeyManager, MintKeyCache, MintRegistration, Mints};
+use super::{KeyManager, MintKeyCache};
 use crate::nuts::Id;
+use crate::wallet::MintConnector;
 use crate::Error;
 
+#[cfg(feature = "auth")]
+use crate::wallet::AuthMintConnector;
+
 /// Messages for the background refresh task
 #[derive(Debug, Clone)]
 pub(super) enum MessageToWorker {
@@ -24,36 +28,34 @@ pub(super) enum MessageToWorker {
 
     /// Make sure the mint is loaded, from either any localstore or remotely.
     ///
-    /// This function also make sure all stores have a copy of the mint info and keys
-    SyncMint(MintUrl),
+    /// This function also makes sure all storages have a copy of the mint info and keys
+    SyncMint,
 
-    /// Fetch keys for a specific mint immediately
-    FetchMint(MintUrl),
+    /// Fetch keys from the mint immediately
+    FetchMint,
 }
 
 impl KeyManager {
     /// Load a specific keyset from database or HTTP
     ///
-    /// First checks all registered databases for the keyset. If not found,
+    /// First checks all databases for the keyset. If not found,
     /// fetches from the mint server via HTTP and persists to all databases.
     pub(super) async fn load_keyset_from_db_or_http(
-        registration: &MintRegistration,
+        mint_url: &MintUrl,
+        storages: &Arc<
+            ParkingLotRwLock<
+                Vec<Arc<dyn WalletDatabase<Err = cdk_common::database::Error> + Send + Sync>>,
+            >,
+        >,
+        client: &Arc<dyn MintConnector + Send + Sync>,
         keyset_id: &Id,
     ) -> Result<KeySet, Error> {
-        let storages = registration
-            .resources
-            .read()
-            .values()
-            .map(|resource| resource.storage.clone())
-            .collect::<Vec<_>>();
-
-        // Try database first
-        for storage in &storages {
+        let storages_list = storages.read().clone();
+
+        // Try all databases first
+        for storage in &storages_list {
             if let Some(keys) = storage.get_keys(keyset_id).await? {
-                debug!(
-                    "Loaded keyset {} from database for {}",
-                    keyset_id, registration.mint_url
-                );
+                tracing::debug!("Loaded keyset {} from database for {}", keyset_id, mint_url);
 
                 // Get keyset info to construct KeySet
                 if let Some(keyset_info) = storage.get_keyset_by_id(keyset_id).await? {
@@ -67,71 +69,59 @@ impl KeyManager {
             }
         }
 
-        // Not in database, fetch from HTTP
-        debug!(
+        // Not in any database, fetch from HTTP
+        tracing::debug!(
             "Keyset {} not in database, fetching from mint server for {}",
-            keyset_id, registration.mint_url
+            keyset_id,
+            mint_url
         );
 
-        let http_client = registration
-            .resources
-            .read()
-            .values()
-            .next()
-            .ok_or(Error::IncorrectMint)?
-            .client
-            .clone();
-
-        let keyset = http_client.get_mint_keyset(*keyset_id).await?;
+        let keyset = client.get_mint_keyset(*keyset_id).await?;
         keyset.verify_id()?;
 
         // Persist to all databases
-        for storage in &storages {
+        for storage in &storages_list {
             let _ = storage.add_keys(keyset.clone()).await.inspect_err(|e| {
-                warn!(
+                tracing::warn!(
                     "Failed to persist keyset {} for {}: {}",
-                    keyset_id, registration.mint_url, e
+                    keyset_id,
+                    mint_url,
+                    e
                 )
             });
         }
 
-        debug!(
-            "Loaded keyset {} from HTTP for {}",
-            keyset_id, registration.mint_url
-        );
+        tracing::debug!("Loaded keyset {} from HTTP for {}", keyset_id, mint_url);
 
         Ok(keyset)
     }
 
-    /// Load mint info and keys from all registered databases
+    /// Load mint info and keys from all databases
     ///
-    /// Iterates through all storage backends and loads keysets and keys into cache.
+    /// Iterates through all storages and loads keysets and keys into cache.
     /// This is called on first access when cache is empty.
     pub(super) async fn fetch_mint_info_and_keys_from_db(
-        registration: &MintRegistration,
-    ) -> Result<(), Error> {
-        debug!(
-            "Cache empty, loading from storage first for {}",
-            registration.mint_url
-        );
+        mint_url: &MintUrl,
+        storages: &Arc<
+            ParkingLotRwLock<
+                Vec<Arc<dyn WalletDatabase<Err = cdk_common::database::Error> + Send + Sync>>,
+            >,
+        >,
+    ) -> Result<MintKeyCache, Error> {
+        tracing::debug!("Cache empty, loading from storage first for {}", mint_url);
 
         let mut storage_cache = MintKeyCache::empty();
-        let storages = registration
-            .resources
-            .read()
-            .values()
-            .map(|resource| resource.storage.clone())
-            .collect::<Vec<_>>();
-
-        for storage in storages {
+        let storages_list = storages.read().clone();
+
+        for storage in storages_list {
             if storage_cache.mint_info.is_none() {
-                storage_cache.mint_info = storage.get_mint(registration.mint_url.clone()).await?;
+                storage_cache.mint_info = storage.get_mint(mint_url.clone()).await?;
             }
 
             for keyset in storage
-                .get_mint_keysets(registration.mint_url.clone())
+                .get_mint_keysets(mint_url.clone())
                 .await?
-                .ok_or(Error::UnknownKeySet)?
+                .unwrap_or_default()
             {
                 if storage_cache.keysets_by_id.contains_key(&keyset.id) {
                     continue;
@@ -158,21 +148,16 @@ impl KeyManager {
             }
         }
 
-        let keys_count = storage_cache.keys_by_id.len();
         storage_cache.refresh_version += 1;
         storage_cache.is_ready = true;
 
-        let storage_cache = Arc::new(storage_cache);
-        registration.cache.store(storage_cache.clone());
-
-        Self::persist_cache(registration, storage_cache).await;
-
-        debug!(
+        tracing::debug!(
             "Loaded {} keys from storage for {}",
-            keys_count, registration.mint_url
+            storage_cache.keys_by_id.len(),
+            mint_url
         );
 
-        Ok(())
+        Ok(storage_cache)
     }
 
     /// Persist cache to a single database
@@ -193,9 +178,10 @@ impl KeyManager {
                 .add_mint(mint_url.clone(), new_cache.mint_info.clone())
                 .await
                 .inspect_err(|e| {
-                    warn!("Failed to persist mint_info for {}: {}", mint_url, e);
+                    tracing::warn!("Failed to persist mint_info for {}: {}", mint_url, e);
                 });
         }
+
         let _ = storage
             .add_mint_keysets(
                 mint_url.clone(),
@@ -206,20 +192,20 @@ impl KeyManager {
                     .collect(),
             )
             .await
-            .inspect_err(|e| warn!("Failed to persist keysets for {}: {}", mint_url, e));
+            .inspect_err(|e| tracing::warn!("Failed to persist keysets for {}: {}", mint_url, e));
 
         for (keyset_id, keys) in new_cache.keys_by_id.iter() {
             if storage
                 .get_keys(keyset_id)
                 .await
-                .inspect_err(|e| warn!("Failed to get_keys {e}"))
+                .inspect_err(|e| tracing::warn!("Failed to get_keys {e}"))
                 .unwrap_or_default()
                 .is_none()
             {
                 let keyset = if let Some(v) = new_cache.keysets_by_id.get(keyset_id) {
                     v
                 } else {
-                    warn!("Malformed keysets, cannot find {}", keyset_id);
+                    tracing::warn!("Malformed keysets, cannot find {}", keyset_id);
                     continue;
                 };
                 let _ = storage
@@ -231,7 +217,7 @@ impl KeyManager {
                     })
                     .await
                     .inspect_err(|e| {
-                        warn!("Failed to persist keys for keyset {}: {}", keyset_id, e)
+                        tracing::warn!("Failed to persist keys for keyset {}: {}", keyset_id, e)
                     });
             }
         }
@@ -241,20 +227,20 @@ impl KeyManager {
     ///
     /// Spawns a task for each storage backend to write cache asynchronously.
     pub(super) async fn persist_cache(
-        registration: &MintRegistration,
+        storages: &Arc<
+            ParkingLotRwLock<
+                Vec<Arc<dyn WalletDatabase<Err = cdk_common::database::Error> + Send + Sync>>,
+            >,
+        >,
+        mint_url: MintUrl,
         new_cache: Arc<MintKeyCache>,
     ) {
-        let storages = registration
-            .resources
-            .read()
-            .values()
-            .map(|x| x.storage.clone())
-            .collect::<Vec<_>>();
-
-        for storage in storages {
+        let storages_list = storages.read().clone();
+
+        for storage in storages_list {
             spawn(Self::persist_cache_db(
                 storage,
-                registration.mint_url.clone(),
+                mint_url.clone(),
                 new_cache.clone(),
             ));
         }
@@ -265,34 +251,20 @@ impl KeyManager {
     /// Fetches mint info, keysets, and keys from the mint server. Updates cache
     /// and schedules next refresh. Persists new data to all databases.
     pub(super) async fn fetch_from_http(
-        registration: MintRegistration,
+        mint_url: MintUrl,
+        client: Arc<dyn MintConnector + Send + Sync>,
+        #[cfg(feature = "auth")] auth_client: Arc<dyn AuthMintConnector + Send + Sync>,
+        storages: Arc<
+            ParkingLotRwLock<
+                Vec<Arc<dyn WalletDatabase<Err = cdk_common::database::Error> + Send + Sync>>,
+            >,
+        >,
+        cache: Arc<ArcSwap<MintKeyCache>>,
         refresh_scheduler: RefreshScheduler,
     ) -> Result<(), Error> {
-        debug!(
-            "Fetching keys from mint server for {}",
-            registration.mint_url
-        );
-
-        let http_client = registration
-            .resources
-            .read()
-            .values()
-            .next()
-            .ok_or(Error::IncorrectMint)?
-            .client
-            .clone();
-
-        #[cfg(feature = "auth")]
-        let http_auth_client = registration
-            .resources
-            .read()
-            .values()
-            .next()
-            .ok_or(Error::IncorrectMint)?
-            .auth_client
-            .clone();
+        tracing::debug!("Fetching keys from mint server for {}", mint_url);
 
-        let mint_info = http_client.get_mint_info().await?;
+        let mint_info = client.get_mint_info().await?;
 
         if let Some(mint_unix_time) = mint_info.time {
             let current_unix_time = unix_time();
@@ -306,20 +278,19 @@ impl KeyManager {
             }
         }
 
-        let keysets_response = http_client.get_mint_keysets().await?;
+        let keysets_response = client.get_mint_keysets().await?;
 
         let keysets = keysets_response.keysets;
 
         #[cfg(feature = "auth")]
-        let keysets = if let Ok(auth_keysets_response) =
-            http_auth_client.get_mint_blind_auth_keysets().await
-        {
-            let mut keysets = keysets;
-            keysets.extend_from_slice(&auth_keysets_response.keysets);
-            keysets
-        } else {
-            keysets
-        };
+        let keysets =
+            if let Ok(auth_keysets_response) = auth_client.get_mint_blind_auth_keysets().await {
+                let mut keysets = keysets;
+                keysets.extend_from_slice(&auth_keysets_response.keysets);
+                keysets
+            } else {
+                keysets
+            };
 
         let mut new_cache = MintKeyCache::empty();
 
@@ -334,143 +305,157 @@ impl KeyManager {
             }
 
             // Try to load keyset from database first, then HTTP
-            if let Ok(keyset) = Self::load_keyset_from_db_or_http(&registration, &keyset_info.id)
-                .await
-                .inspect_err(|e| {
-                    warn!(
-                        "Failed to load keyset {} for {}: {}",
-                        keyset_info.id, registration.mint_url, e
-                    )
-                })
+            if let Ok(keyset) =
+                Self::load_keyset_from_db_or_http(&mint_url, &storages, &client, &keyset_info.id)
+                    .await
+                    .inspect_err(|e| {
+                        tracing::warn!(
+                            "Failed to load keyset {} for {}: {}",
+                            keyset_info.id,
+                            mint_url,
+                            e
+                        )
+                    })
             {
                 let keys = Arc::new(keyset.keys.clone());
                 new_cache.keys_by_id.insert(keyset_info.id, keys);
             }
         }
 
-        refresh_scheduler.schedule_refresh(registration.mint_url.clone());
+        refresh_scheduler.schedule_refresh(mint_url.clone());
 
-        let old_generation = registration.cache.load().refresh_version;
+        let old_generation = cache.load().refresh_version;
         new_cache.mint_info = Some(mint_info);
         new_cache.refresh_version = old_generation + 1;
         new_cache.is_ready = true;
         new_cache.last_refresh = std::time::Instant::now();
 
-        debug!(
+        tracing::debug!(
             "Refreshed {} keysets and {} keys for {} (generation {})",
             new_cache.keysets_by_id.len(),
             new_cache.keys_by_id.len(),
-            registration.mint_url,
+            mint_url,
             new_cache.refresh_version
         );
 
         let new_cache = Arc::new(new_cache);
-        Self::persist_cache(&registration, new_cache.clone()).await;
-        registration.cache.store(new_cache);
+        Self::persist_cache(&storages, mint_url, new_cache.clone()).await;
+        cache.store(new_cache);
 
         Ok::<(), Error>(())
     }
 
     pub(super) fn sync_mint_task(
-        registration: MintRegistration,
+        mint_url: MintUrl,
+        client: Arc<dyn MintConnector + Send + Sync>,
+        #[cfg(feature = "auth")] auth_client: Arc<dyn AuthMintConnector + Send + Sync>,
+        storages: Arc<
+            ParkingLotRwLock<
+                Vec<Arc<dyn WalletDatabase<Err = cdk_common::database::Error> + Send + Sync>>,
+            >,
+        >,
+        cache: Arc<ArcSwap<MintKeyCache>>,
         refresh_scheduler: RefreshScheduler,
     ) {
         spawn(async move {
-            if !registration.cache.load().is_ready {
-                let _ = Self::fetch_mint_info_and_keys_from_db(&registration)
+            if !cache.load().is_ready {
+                if let Ok(new_cache) = Self::fetch_mint_info_and_keys_from_db(&mint_url, &storages)
                     .await
                     .inspect_err(|e| {
-                        warn!(
-                            "Failed to load keys from storage for {}: {}",
-                            registration.mint_url, e
-                        )
-                    });
+                        tracing::warn!("Failed to load keys from storage for {}: {}", mint_url, e)
+                    })
+                {
+                    let new_cache = Arc::new(new_cache);
+                    Self::persist_cache(&storages, mint_url.clone(), new_cache.clone()).await;
+                    cache.store(new_cache);
+                }
             }
 
-            if !registration.cache.load().is_ready {
-                let mint_url = registration.mint_url.clone();
+            if !cache.load().is_ready {
                 let _ = tokio::time::timeout(
                     Duration::from_secs(60),
-                    Self::fetch_from_http(registration, refresh_scheduler),
+                    Self::fetch_from_http(
+                        mint_url.clone(),
+                        client,
+                        #[cfg(feature = "auth")]
+                        auth_client,
+                        storages,
+                        cache,
+                        refresh_scheduler,
+                    ),
                 )
                 .await
-                .inspect_err(|e| warn!("Failed to fetch keys for {} with error {}", mint_url, e));
+                .inspect_err(|e| {
+                    tracing::warn!("Failed to fetch keys for {} with error {}", mint_url, e)
+                });
             }
         });
     }
 
     /// Refresh keys from mint server
     ///
-    /// Spawns an async task with 60s timeout. HTTP requests are limited to
-    /// MAX_CONCURRENT_HTTP_REQUESTS concurrent requests via semaphore.
+    /// Spawns an async task with 60s timeout.
     pub(super) fn fetch_and_sync_mint_task(
-        registration: MintRegistration,
-        semaphore: Arc<Semaphore>,
+        mint_url: MintUrl,
+        client: Arc<dyn MintConnector + Send + Sync>,
+        #[cfg(feature = "auth")] auth_client: Arc<dyn AuthMintConnector + Send + Sync>,
+        storages: Arc<
+            ParkingLotRwLock<
+                Vec<Arc<dyn WalletDatabase<Err = cdk_common::database::Error> + Send + Sync>>,
+            >,
+        >,
+        cache: Arc<ArcSwap<MintKeyCache>>,
         refresh_scheduler: RefreshScheduler,
     ) {
         spawn(async move {
-            if !registration.cache.load().is_ready {
-                let _ = Self::fetch_mint_info_and_keys_from_db(&registration)
+            if !cache.load().is_ready {
+                if let Ok(new_cache) = Self::fetch_mint_info_and_keys_from_db(&mint_url, &storages)
                     .await
                     .inspect_err(|e| {
-                        warn!(
-                            "Failed to load keys from storage for {}: {}",
-                            registration.mint_url, e
-                        )
-                    });
+                        tracing::warn!("Failed to load keys from storage for {}: {}", mint_url, e)
+                    })
+                {
+                    let new_cache = Arc::new(new_cache);
+                    Self::persist_cache(&storages, mint_url.clone(), new_cache.clone()).await;
+                    cache.store(new_cache);
+                }
             }
 
-            let Ok(http_permit) = semaphore.acquire().await.inspect_err(|e| {
-                error!(
-                    "Failed to acquire HTTP permit for {}: {}",
-                    registration.mint_url, e
-                );
-            }) else {
-                return;
-            };
-
-            debug!(
-                "Acquired HTTP permit for {} ({} available)",
-                registration.mint_url,
-                semaphore.available_permits()
-            );
-
-            let mint_url = registration.mint_url.clone();
-
             let result = tokio::time::timeout(
                 Duration::from_secs(60),
-                Self::fetch_from_http(registration, refresh_scheduler),
+                Self::fetch_from_http(
+                    mint_url.clone(),
+                    client,
+                    #[cfg(feature = "auth")]
+                    auth_client,
+                    storages,
+                    cache,
+                    refresh_scheduler,
+                ),
             )
             .await;
 
-            drop(http_permit);
-
             let _ = result
                 .map_err(|_| Error::Timeout)
                 .and_then(|r| r)
                 .inspect(|_| {
-                    debug!(
+                    tracing::debug!(
                         "Successfully fetched keys from mint server for {}",
                         mint_url
                     );
                 })
                 .inspect_err(|e| match e {
                     Error::Timeout => {
-                        error!("Timeout fetching keys from mint server for {}", mint_url)
+                        tracing::error!("Timeout fetching keys from mint server for {}", mint_url)
                     }
                     _ => {
-                        error!(
+                        tracing::error!(
                             "Failed to fetch keys from mint server for {}: {}",
-                            mint_url, e
+                            mint_url,
+                            e
                         )
                     }
                 });
-
-            debug!(
-                "Released HTTP permit for {} ({} available)",
-                mint_url,
-                semaphore.available_permits()
-            );
         });
     }
 
@@ -480,75 +465,76 @@ impl KeyManager {
     /// All refresh operations are spawned as separate tasks with timeouts.
     pub(super) async fn refresh_loop(
         mut rx: tokio::sync::mpsc::UnboundedReceiver<MessageToWorker>,
-        mints: Mints,
+        mint_url: MintUrl,
+        client: Arc<dyn MintConnector + Send + Sync>,
+        #[cfg(feature = "auth")] auth_client: Arc<dyn AuthMintConnector + Send + Sync>,
+        storages: Arc<
+            ParkingLotRwLock<
+                Vec<Arc<dyn WalletDatabase<Err = cdk_common::database::Error> + Send + Sync>>,
+            >,
+        >,
+        cache: Arc<ArcSwap<MintKeyCache>>,
         refresh_interval: Duration,
-        semaphore: Arc<Semaphore>,
     ) {
         let mut interval = tokio::time::interval(Duration::from_secs(1));
         let refresh_scheduler = RefreshScheduler::new(refresh_interval);
+        let mut is_scheduled = false;
 
         loop {
             tokio::select! {
                 Some(msg) = rx.recv() => {
                     match msg {
                         MessageToWorker::Stop => {
-                            debug!("Stopping refresh task");
+                            tracing::debug!("Stopping refresh task for {}", mint_url);
                             break;
                         }
-                        MessageToWorker::SyncMint(mint_url) => {
-                            debug!("Sync all instances of {}", mint_url);
-                            let registration = {
-                                let mints_lock = mints.read();
-                                mints_lock.get(&mint_url).cloned()
-                            };
-
-                            if let Some(reg) = registration {
-                                Self::sync_mint_task(reg, refresh_scheduler.clone());
-                            } else {
-                                warn!("FetchMint: Mint not registered: {}", mint_url);
-                            }
+                        MessageToWorker::SyncMint => {
+                            tracing::debug!("Sync mint {}", mint_url);
+                            Self::sync_mint_task(
+                                mint_url.clone(),
+                                client.clone(),
+                                #[cfg(feature = "auth")]
+                                auth_client.clone(),
+                                storages.clone(),
+                                cache.clone(),
+                                refresh_scheduler.clone(),
+                            );
                         }
-                        MessageToWorker::FetchMint(mint_url) => {
-                            debug!("FetchMint message received for {}", mint_url);
-                            let registration = {
-                                let mints_lock = mints.read();
-                                mints_lock.get(&mint_url).cloned()
-                            };
-
-                            if let Some(reg) = registration {
-                                Self::fetch_and_sync_mint_task(reg, semaphore.clone(), refresh_scheduler.clone());
-                            } else {
-                                warn!("FetchMint: Mint not registered: {}", mint_url);
-                            }
+                        MessageToWorker::FetchMint => {
+                            tracing::debug!("FetchMint message received for {}", mint_url);
+                            Self::fetch_and_sync_mint_task(
+                                mint_url.clone(),
+                                client.clone(),
+                                #[cfg(feature = "auth")]
+                                auth_client.clone(),
+                                storages.clone(),
+                                cache.clone(),
+                                refresh_scheduler.clone(),
+                            );
                         }
                     }
                 }
 
                 _ = interval.tick() => {
-                    debug!("Checking for mints due for refresh");
-
                     let due_mints = refresh_scheduler.get_due_refreshes();
 
-                    if !due_mints.is_empty() {
-                        debug!("Found {} mints due for refresh", due_mints.len());
-                    }
-
-                    for mint_url in due_mints {
-                        let registration = {
-                            let mints_lock = mints.read();
-                            mints_lock.get(&mint_url).cloned()
-                        };
-
-                        if let Some(reg) = registration {
-                            Self::fetch_and_sync_mint_task(reg, semaphore.clone(), refresh_scheduler.clone());
-                        } else {
-                            warn!("Mint no longer registered: {}", mint_url);
-                        }
+                    if !due_mints.is_empty() || !is_scheduled {
+                        tracing::debug!("Time to refresh mint: {}", mint_url);
+                        Self::fetch_and_sync_mint_task(
+                            mint_url.clone(),
+                            client.clone(),
+                            #[cfg(feature = "auth")]
+                            auth_client.clone(),
+                            storages.clone(),
+                            cache.clone(),
+                            refresh_scheduler.clone(),
+                        );
+                        is_scheduled = true;
                     }
                 }
             }
         }
 
-        debug!("Refresh loop stopped");
+        tracing::debug!("Refresh loop stopped for {}", mint_url);
     }
 }

+ 6 - 6
crates/cdk/src/wallet/keysets.rs

@@ -17,7 +17,7 @@ impl Wallet {
     pub async fn load_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> {
         Ok((*self
             .key_manager
-            .get_keys(&self.mint_url, &keyset_id)
+            .get_keys(&keyset_id)
             .await?)
             .clone())
     }
@@ -30,7 +30,7 @@ impl Wallet {
     /// but will fall back to online if needed.
     #[instrument(skip(self))]
     pub async fn load_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
-        self.key_manager.get_keysets(&self.mint_url).await
+        self.key_manager.get_keysets().await
     }
 
     /// Get keysets from KeyManager cache only - pure offline operation
@@ -42,7 +42,7 @@ impl Wallet {
     pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
         let keysets = self
             .key_manager
-            .get_keysets(&self.mint_url)
+            .get_keysets()
             .await?
             .into_iter()
             .filter(|k| k.unit != CurrencyUnit::Auth)
@@ -66,7 +66,7 @@ impl Wallet {
 
         let keysets = self
             .key_manager
-            .refresh(&self.mint_url)
+            .refresh()
             .await?
             .into_iter()
             .filter(|k| self.unit == k.unit && k.active)
@@ -101,7 +101,7 @@ impl Wallet {
     /// returns an error. Use this for offline operations or when you want to avoid network calls.
     #[instrument(skip(self))]
     pub async fn get_active_keyset(&self) -> Result<KeySetInfo, Error> {
-        let active_keysets = self.key_manager.get_active_keysets(&self.mint_url).await?;
+        let active_keysets = self.key_manager.get_active_keysets().await?;
 
         active_keysets
             .into_iter()
@@ -116,7 +116,7 @@ impl Wallet {
     /// from the KeyManager cache. This is an offline operation that does not contact the mint.
     /// If no keysets are cached, returns an error.
     pub async fn get_keyset_fees_and_amounts(&self) -> Result<KeysetFeeAndAmounts, Error> {
-        let keysets = self.key_manager.get_keysets(&self.mint_url).await?;
+        let keysets = self.key_manager.get_keysets().await?;
 
         let mut fees = HashMap::new();
         for keyset in keysets {

+ 3 - 5
crates/cdk/src/wallet/mod.rs

@@ -8,7 +8,6 @@ use cdk_common::amount::FeeAndAmounts;
 use cdk_common::database::{self, WalletDatabase};
 use cdk_common::subscription::WalletParams;
 use getrandom::getrandom;
-use key_manager::KeySubscription;
 use subscription::{ActiveSubscription, SubscriptionManager};
 #[cfg(feature = "auth")]
 use tokio::sync::RwLock;
@@ -86,11 +85,10 @@ pub struct Wallet {
     pub unit: CurrencyUnit,
     /// Storage backend
     pub localstore: Arc<dyn WalletDatabase<Err = database::Error> + Send + Sync>,
-    /// Centralized key manager (lock-free cached key access)
+    /// Key manager for this mint (lock-free cached key access)
     pub key_manager: Arc<key_manager::KeyManager>,
     /// The targeted amount of proofs to have at each size
     pub target_proof_count: usize,
-    _key_sub_id: Arc<KeySubscription>,
     #[cfg(feature = "auth")]
     auth_wallet: Arc<RwLock<Option<AuthWallet>>>,
     seed: [u8; 64],
@@ -223,7 +221,7 @@ impl Wallet {
         for keyset_id in proofs_per_keyset.keys() {
             let mint_keyset_info = self
                 .key_manager
-                .get_keyset_by_id(&self.mint_url, keyset_id)
+                .get_keyset_by_id(keyset_id)
                 .await?;
             fee_per_keyset.insert(*keyset_id, mint_keyset_info.input_fee_ppk);
         }
@@ -238,7 +236,7 @@ impl Wallet {
     pub async fn get_keyset_count_fee(&self, keyset_id: &Id, count: u64) -> Result<Amount, Error> {
         let input_fee_ppk = self
             .key_manager
-            .get_keyset_by_id(&self.mint_url, keyset_id)
+            .get_keyset_by_id(keyset_id)
             .await?
             .input_fee_ppk;
 

+ 1 - 1
crates/cdk/src/wallet/swap.rs

@@ -46,7 +46,7 @@ impl Wallet {
 
         let active_keys = self
             .key_manager
-            .get_keys(&self.mint_url, &active_keyset_id)
+            .get_keys(&active_keyset_id)
             .await?;
 
         let post_swap_proofs = construct_proofs(