Просмотр исходного кода

feat: config wallet ttl deafult to 1 hour (#1534)

tsk 1 неделя назад
Родитель
Сommit
dac98b9775

+ 4 - 0
crates/cdk/Cargo.toml

@@ -164,6 +164,10 @@ required-features = ["npubcash"]
 name = "multimint-npubcash"
 name = "multimint-npubcash"
 required-features = ["npubcash"]
 required-features = ["npubcash"]
 
 
+[[example]]
+name = "configure_wallet"
+required-features = ["wallet"]
+
 [dev-dependencies]
 [dev-dependencies]
 rand.workspace = true
 rand.workspace = true
 cdk-sqlite.workspace = true
 cdk-sqlite.workspace = true

+ 78 - 0
crates/cdk/examples/configure_wallet.rs

@@ -0,0 +1,78 @@
+//! Example of configuring the wallet with custom settings, including metadata cache TTL.
+//!
+//! This example demonstrates:
+//! 1. Creating a Wallet with a custom metadata cache TTL
+//! 2. Creating a MultiMintWallet and adding a mint with a custom configuration
+//! 3. Updating the configuration of an active wallet
+
+use std::str::FromStr;
+use std::sync::Arc;
+use std::time::Duration;
+
+use cdk::mint_url::MintUrl;
+use cdk::nuts::CurrencyUnit;
+use cdk::wallet::multi_mint_wallet::WalletConfig;
+use cdk::wallet::{MultiMintWallet, WalletBuilder};
+use cdk_sqlite::wallet::memory;
+use rand::random;
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    // Generate a random seed
+    let seed = random::<[u8; 64]>();
+    let unit = CurrencyUnit::Sat;
+    let localstore = Arc::new(memory::empty().await?);
+
+    // ==========================================
+    // 1. Configure a single Wallet
+    // ==========================================
+    println!("\n=== Single Wallet Configuration ===");
+    let mint_url = MintUrl::from_str("https://fake.thesimplekid.dev")?;
+
+    // Create a wallet with a custom 10-minute TTL (default is 1 hour)
+    let wallet = WalletBuilder::new()
+        .mint_url(mint_url.clone())
+        .unit(unit.clone())
+        .localstore(localstore.clone())
+        .seed(seed)
+        .set_metadata_cache_ttl(Some(Duration::from_secs(600))) // 10 minutes
+        .build()?;
+
+    println!("Created wallet with 10 minute metadata cache TTL");
+
+    // You can also update the TTL on an existing wallet
+    wallet.set_metadata_cache_ttl(Some(Duration::from_secs(300))); // Change to 5 minutes
+    println!("Updated wallet TTL to 5 minutes");
+
+    // ==========================================
+    // 2. Configure MultiMintWallet
+    // ==========================================
+    println!("\n=== MultiMintWallet Configuration ===");
+
+    // Create the MultiMintWallet
+    let multi_wallet = MultiMintWallet::new(localstore.clone(), seed, unit.clone()).await?;
+
+    // Define configuration for a new mint
+    // This config uses a very short 1-minute TTL
+    let config = WalletConfig::new().with_metadata_cache_ttl(Some(Duration::from_secs(60)));
+
+    let mint_url_2 = MintUrl::from_str("https://testnut.cashu.space")?;
+
+    // Add the mint with the custom configuration
+    multi_wallet
+        .add_mint_with_config(mint_url_2.clone(), config.clone())
+        .await?;
+    println!("Added mint {} with 1 minute TTL", mint_url_2);
+
+    // Update configuration for an existing mint
+    // Let's disable auto-refresh (set to None) for the first mint
+    let no_refresh_config = WalletConfig::new().with_metadata_cache_ttl(None); // Never expire
+
+    multi_wallet.add_mint(mint_url.clone()).await?; // Add first mint with default settings
+    multi_wallet
+        .set_mint_config(mint_url.clone(), no_refresh_config)
+        .await?;
+    println!("Updated mint {} to never expire metadata cache", mint_url);
+
+    Ok(())
+}

+ 19 - 1
crates/cdk/src/wallet/builder.rs

@@ -55,7 +55,7 @@ impl Default for WalletBuilder {
             auth_wallet: None,
             auth_wallet: None,
             seed: None,
             seed: None,
             client: None,
             client: None,
-            metadata_cache_ttl: None,
+            metadata_cache_ttl: Some(Duration::from_secs(3600)),
             use_http_subscription: false,
             use_http_subscription: false,
             metadata_cache: None,
             metadata_cache: None,
             metadata_caches: HashMap::new(),
             metadata_caches: HashMap::new(),
@@ -76,6 +76,13 @@ impl WalletBuilder {
     }
     }
 
 
     /// Set metadata_cache_ttl
     /// Set metadata_cache_ttl
+    ///
+    /// The TTL determines how often the wallet checks the mint for new keysets and information.
+    ///
+    /// If `None`, the cache will never expire and the wallet will use cached data indefinitely
+    /// (unless manually refreshed).
+    ///
+    /// The default value is 1 hour (3600 seconds).
     pub fn set_metadata_cache_ttl(mut self, metadata_cache_ttl: Option<Duration>) -> Self {
     pub fn set_metadata_cache_ttl(mut self, metadata_cache_ttl: Option<Duration>) -> Self {
         self.metadata_cache_ttl = metadata_cache_ttl;
         self.metadata_cache_ttl = metadata_cache_ttl;
         self
         self
@@ -261,3 +268,14 @@ impl WalletBuilder {
         })
         })
     }
     }
 }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_default_ttl() {
+        let builder = WalletBuilder::default();
+        assert_eq!(builder.metadata_cache_ttl, Some(Duration::from_secs(3600)));
+    }
+}

+ 7 - 0
crates/cdk/src/wallet/mint_metadata_cache.rs

@@ -143,6 +143,13 @@ impl std::fmt::Debug for MintMetadataCache {
 
 
 impl Wallet {
 impl Wallet {
     /// Sets the metadata cache TTL
     /// Sets the metadata cache TTL
+    ///
+    /// The TTL determines how often the wallet checks the mint for new keysets and information.
+    ///
+    /// If `None`, the cache will never expire and the wallet will use cached data indefinitely
+    /// (unless manually refreshed).
+    ///
+    /// The default value is 1 hour (3600 seconds).
     pub fn set_metadata_cache_ttl(&self, ttl: Option<Duration>) {
     pub fn set_metadata_cache_ttl(&self, ttl: Option<Duration>) {
         let mut guarded_ttl = self.metadata_cache_ttl.write();
         let mut guarded_ttl = self.metadata_cache_ttl.write();
         *guarded_ttl = ttl;
         *guarded_ttl = ttl;

+ 68 - 11
crates/cdk/src/wallet/multi_mint_wallet.rs

@@ -92,6 +92,15 @@ pub struct WalletConfig {
     pub auth_connector: Option<Arc<dyn super::auth::AuthMintConnector + Send + Sync>>,
     pub auth_connector: Option<Arc<dyn super::auth::AuthMintConnector + Send + Sync>>,
     /// Target number of proofs to maintain at each denomination
     /// Target number of proofs to maintain at each denomination
     pub target_proof_count: Option<usize>,
     pub target_proof_count: Option<usize>,
+    /// Metadata cache TTL
+    ///
+    /// The TTL determines how often the wallet checks the mint for new keysets and information.
+    ///
+    /// If `None`, the cache will never expire and the wallet will use cached data indefinitely
+    /// (unless manually refreshed).
+    ///
+    /// The default value is 1 hour (3600 seconds).
+    pub metadata_cache_ttl: Option<std::time::Duration>,
 }
 }
 
 
 impl WalletConfig {
 impl WalletConfig {
@@ -124,6 +133,19 @@ impl WalletConfig {
         self.target_proof_count = Some(count);
         self.target_proof_count = Some(count);
         self
         self
     }
     }
+
+    /// Set metadata cache TTL
+    ///
+    /// The TTL determines how often the wallet checks the mint for new keysets and information.
+    ///
+    /// If `None`, the cache will never expire and the wallet will use cached data indefinitely
+    /// (unless manually refreshed).
+    ///
+    /// The default value is 1 hour (3600 seconds).
+    pub fn with_metadata_cache_ttl(mut self, ttl: Option<std::time::Duration>) -> Self {
+        self.metadata_cache_ttl = ttl;
+        self
+    }
 }
 }
 
 
 /// Multi Mint Wallet
 /// Multi Mint Wallet
@@ -346,6 +368,11 @@ impl MultiMintWallet {
                     wallet.set_client(connector);
                     wallet.set_client(connector);
                 }
                 }
 
 
+                // Update metadata cache TTL if provided
+                if let Some(ttl) = config.metadata_cache_ttl {
+                    wallet.set_metadata_cache_ttl(Some(ttl));
+                }
+
                 // TODO: Handle auth_connector if provided
                 // TODO: Handle auth_connector if provided
                 #[cfg(feature = "auth")]
                 #[cfg(feature = "auth")]
                 if let Some(_auth_connector) = config.auth_connector {
                 if let Some(_auth_connector) = config.auth_connector {
@@ -402,7 +429,7 @@ impl MultiMintWallet {
         if let Some(cfg) = config {
         if let Some(cfg) = config {
             if let Some(custom_connector) = &cfg.mint_connector {
             if let Some(custom_connector) = &cfg.mint_connector {
                 // Use custom connector with WalletBuilder
                 // Use custom connector with WalletBuilder
-                let builder = WalletBuilder::new()
+                let mut builder = WalletBuilder::new()
                     .mint_url(mint_url.clone())
                     .mint_url(mint_url.clone())
                     .unit(self.unit.clone())
                     .unit(self.unit.clone())
                     .localstore(self.localstore.clone())
                     .localstore(self.localstore.clone())
@@ -410,6 +437,10 @@ impl MultiMintWallet {
                     .target_proof_count(cfg.target_proof_count.unwrap_or(3))
                     .target_proof_count(cfg.target_proof_count.unwrap_or(3))
                     .shared_client(custom_connector.clone());
                     .shared_client(custom_connector.clone());
 
 
+                if let Some(ttl) = cfg.metadata_cache_ttl {
+                    builder = builder.set_metadata_cache_ttl(Some(ttl));
+                }
+
                 // TODO: Handle auth_connector if provided
                 // TODO: Handle auth_connector if provided
                 #[cfg(feature = "auth")]
                 #[cfg(feature = "auth")]
                 if let Some(_auth_connector) = &cfg.auth_connector {
                 if let Some(_auth_connector) = &cfg.auth_connector {
@@ -424,6 +455,7 @@ impl MultiMintWallet {
 
 
         // Fall back to existing logic: proxy/Tor/default
         // Fall back to existing logic: proxy/Tor/default
         let target_proof_count = config.and_then(|c| c.target_proof_count).unwrap_or(3);
         let target_proof_count = config.and_then(|c| c.target_proof_count).unwrap_or(3);
+        let metadata_cache_ttl = config.and_then(|c| c.metadata_cache_ttl);
 
 
         let wallet = if let Some(proxy_url) = &self.proxy_config {
         let wallet = if let Some(proxy_url) = &self.proxy_config {
             // Create wallet with proxy-configured client
             // Create wallet with proxy-configured client
@@ -443,14 +475,19 @@ impl MultiMintWallet {
                     crate::wallet::HttpClient::new(mint_url.clone())
                     crate::wallet::HttpClient::new(mint_url.clone())
                 }
                 }
             });
             });
-            WalletBuilder::new()
+            let mut builder = WalletBuilder::new()
                 .mint_url(mint_url.clone())
                 .mint_url(mint_url.clone())
                 .unit(self.unit.clone())
                 .unit(self.unit.clone())
                 .localstore(self.localstore.clone())
                 .localstore(self.localstore.clone())
                 .seed(self.seed)
                 .seed(self.seed)
                 .target_proof_count(target_proof_count)
                 .target_proof_count(target_proof_count)
-                .client(client)
-                .build()?
+                .client(client);
+
+            if let Some(ttl) = metadata_cache_ttl {
+                builder = builder.set_metadata_cache_ttl(Some(ttl));
+            }
+
+            builder.build()?
         } else {
         } else {
             #[cfg(all(feature = "tor", not(target_arch = "wasm32")))]
             #[cfg(all(feature = "tor", not(target_arch = "wasm32")))]
             if let Some(tor) = &self.shared_tor_transport {
             if let Some(tor) = &self.shared_tor_transport {
@@ -471,35 +508,48 @@ impl MultiMintWallet {
                     }
                     }
                 };
                 };
 
 
-                WalletBuilder::new()
+                let mut builder = WalletBuilder::new()
                     .mint_url(mint_url.clone())
                     .mint_url(mint_url.clone())
                     .unit(self.unit.clone())
                     .unit(self.unit.clone())
                     .localstore(self.localstore.clone())
                     .localstore(self.localstore.clone())
                     .seed(self.seed)
                     .seed(self.seed)
                     .target_proof_count(target_proof_count)
                     .target_proof_count(target_proof_count)
-                    .client(client)
-                    .build()?
+                    .client(client);
+
+                if let Some(ttl) = metadata_cache_ttl {
+                    builder = builder.set_metadata_cache_ttl(Some(ttl));
+                }
+
+                builder.build()?
             } else {
             } else {
                 // Create wallet with default client
                 // Create wallet with default client
-                Wallet::new(
+                let wallet = Wallet::new(
                     &mint_url.to_string(),
                     &mint_url.to_string(),
                     self.unit.clone(),
                     self.unit.clone(),
                     self.localstore.clone(),
                     self.localstore.clone(),
                     self.seed,
                     self.seed,
                     Some(target_proof_count),
                     Some(target_proof_count),
-                )?
+                )?;
+                if let Some(ttl) = metadata_cache_ttl {
+                    wallet.set_metadata_cache_ttl(Some(ttl));
+                }
+                wallet
             }
             }
 
 
             #[cfg(not(all(feature = "tor", not(target_arch = "wasm32"))))]
             #[cfg(not(all(feature = "tor", not(target_arch = "wasm32"))))]
             {
             {
                 // Create wallet with default client
                 // Create wallet with default client
-                Wallet::new(
+                let wallet = Wallet::new(
                     &mint_url.to_string(),
                     &mint_url.to_string(),
                     self.unit.clone(),
                     self.unit.clone(),
                     self.localstore.clone(),
                     self.localstore.clone(),
                     self.seed,
                     self.seed,
                     Some(target_proof_count),
                     Some(target_proof_count),
-                )?
+                )?;
+                if let Some(ttl) = metadata_cache_ttl {
+                    wallet.set_metadata_cache_ttl(Some(ttl));
+                }
+                wallet
             }
             }
         };
         };
 
 
@@ -2455,4 +2505,11 @@ mod tests {
         };
         };
         assert!(token_data_no_memo.memo.is_none());
         assert!(token_data_no_memo.memo.is_none());
     }
     }
+
+    #[tokio::test]
+    async fn test_wallet_config_metadata_ttl() {
+        let ttl = std::time::Duration::from_secs(12345);
+        let config = WalletConfig::new().with_metadata_cache_ttl(Some(ttl));
+        assert_eq!(config.metadata_cache_ttl, Some(ttl));
+    }
 }
 }