| 
					
				 | 
			
			
				@@ -1,17 +1,18 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use std::collections::HashMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use cdk_common::nut02::{KeySetInfos, KeySetInfosMethods}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use tracing::instrument; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use crate::nuts::{Id, KeySetInfo, Keys}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use crate::{Error, Wallet}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 impl Wallet { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Get keys for mint keyset 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Fetch keys for mint keyset 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Selected keys from localstore if they are already known 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// If they are not known queries mint for keyset id and stores the [`Keys`] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Returns keys from local database if they are already stored. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// If keys are not found locally, goes online to query the mint for the keyset and stores the [`Keys`] in local database. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    pub async fn get_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pub async fn fetch_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             keys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } else { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -27,10 +28,12 @@ impl Wallet { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(keys) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Get keysets from DB or fetch them 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Get keysets from local database or go online if missing 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Checks the database for keysets and queries the Mint if 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// it can't find any. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// First checks the local database for cached keysets. If keysets are not found locally, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// goes online to refresh keysets from the mint and updates the local database. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// This is the main method for getting keysets in token operations that can work offline 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// but will fall back to online if needed. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pub async fn load_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         match self 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -39,86 +42,105 @@ impl Wallet { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .await? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Some(keysets_info) => Ok(keysets_info), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            None => self.get_mint_keysets().await, // Hit the keysets endpoint if we don't have the keysets for this Mint 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            None => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // If we don't have any keysets, fetch them from the mint 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                let keysets = self.refresh_keysets().await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Ok(keysets) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Get keysets for wallet's mint 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Get keysets from local database only - pure offline operation 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Queries mint for all keysets 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Only checks the local database for cached keysets. If keysets are not found locally, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// returns an error without going online. This is used for operations that must remain 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// offline and rely on previously cached keyset data. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let keysets = self.client.get_mint_keysets().await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.localstore 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Ok(keysets.keysets) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        match self 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .localstore 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .get_mint_keysets(self.mint_url.clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .await? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Some(keysets_info) => Ok(keysets_info), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            None => Err(Error::UnknownKeySet), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Get active keyset for mint 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Refresh keysets by fetching the latest from mint - always goes online 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Queries mint for current keysets then gets [`Keys`] for any unknown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// keysets 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// This method always goes online to fetch the latest keyset information from the mint. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// It updates the local database with the fetched keysets and ensures we have keys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// for all active keysets. This is used when operations need the most up-to-date 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// keyset information and are willing to go online. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    pub async fn get_active_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let keysets = self.client.get_mint_keysets().await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let keysets = keysets.keysets; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pub async fn refresh_keysets(&self) -> Result<KeySetInfos, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tracing::debug!("Refreshing keysets and ensuring we have keys"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let _ = self.get_mint_info().await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // Fetch all current keysets from mint 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let keysets_response = self.client.get_mint_keysets().await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let all_keysets = keysets_response.keysets; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // Update local storage with keyset info 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.localstore 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .add_mint_keysets(self.mint_url.clone(), keysets.clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .add_mint_keysets(self.mint_url.clone(), all_keysets.clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let active_keysets = keysets 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .clone() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .into_iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .filter(|k| k.active && k.unit == self.unit) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .collect::<Vec<KeySetInfo>>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // Filter for active keysets matching our unit 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let keysets: KeySetInfos = all_keysets.unit(self.unit.clone()).cloned().collect(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        match self 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .localstore 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .get_mint_keysets(self.mint_url.clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .await? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            Some(known_keysets) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let unknown_keysets: Vec<&KeySetInfo> = keysets 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    .iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    .filter(|k| known_keysets.contains(k)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    .collect(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for keyset in unknown_keysets { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    self.get_keyset_keys(keyset.id).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            None => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for keyset in keysets { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    self.get_keyset_keys(keyset.id).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // Ensure we have keys for all active keysets 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for keyset in &keysets { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if self.localstore.get_keys(&keyset.id).await?.is_none() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                tracing::debug!("Fetching missing keys for keyset {}", keyset.id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                self.fetch_keyset_keys(keyset.id).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Ok(active_keysets) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Ok(keysets) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Get active keyset for mint with the lowest fees 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Get the active keyset with the lowest fees - always goes online 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     /// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Queries mint for current keysets then gets [`Keys`] for any unknown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// keysets 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// This method always goes online to refresh keysets from the mint and then returns 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// the active keyset with the minimum input fees. Use this when you need the most 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// up-to-date keyset information for operations. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    pub async fn get_active_mint_keyset(&self) -> Result<KeySetInfo, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // Important 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let _ = self.get_mint_info().await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let active_keysets = self.get_active_mint_keysets().await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pub async fn fetch_active_keyset(&self) -> Result<KeySetInfo, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.refresh_keysets() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .await? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .active() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .min_by_key(|k| k.input_fee_ppk) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .cloned() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .ok_or(Error::NoActiveKeyset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let keyset_with_lowest_fee = active_keysets 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .into_iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .min_by_key(|key| key.input_fee_ppk) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            .ok_or(Error::NoActiveKeyset)?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Ok(keyset_with_lowest_fee) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Get the active keyset with the lowest fees from local database only - offline operation 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Returns the active keyset with minimum input fees from cached keysets in the local database. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// This is an offline operation that does not contact the mint. If no keysets are found locally, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// 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> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        match self 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .localstore 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .get_mint_keysets(self.mint_url.clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .await? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Some(keysets_info) => keysets_info 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .into_iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .min_by_key(|k| k.input_fee_ppk) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .ok_or(Error::NoActiveKeyset), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            None => Err(Error::UnknownKeySet), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Get keyset fees for mint 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Get keyset fees for mint from local database only - offline operation 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Returns a HashMap of keyset IDs to their input fee rates (per-proof-per-thousand) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// from cached keysets in the local database. This is an offline operation that does 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// not contact the mint. If no keysets are found locally, returns an error. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pub async fn get_keyset_fees(&self) -> Result<HashMap<Id, u64>, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let keysets = self 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .localstore 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -134,7 +156,11 @@ impl Wallet { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(fees) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// Get keyset fees for mint by keyset id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Get keyset fees for mint by keyset id from local database only - offline operation 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// Returns the input fee rate (per-proof-per-thousand) for a specific keyset ID from 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// cached keysets in the local database. This is an offline operation that does not 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /// contact the mint. If the keyset is not found locally, returns an error. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     pub async fn get_keyset_fees_by_id(&self, keyset_id: Id) -> Result<u64, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.get_keyset_fees() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .await? 
			 |