Переглянути джерело

docs: add better example docs of wallet start up (#1612)

tsk 1 тиждень тому
батько
коміт
89c91f6c6c

+ 8 - 3
crates/cdk/README.md

@@ -55,9 +55,7 @@ use cdk::amount::SplitTarget;
 use cdk_sqlite::wallet::memory;
 use cdk::nuts::{CurrencyUnit, MintQuoteState, PaymentMethod};
 #[cfg(feature = "wallet")]
-use cdk::wallet::Wallet;
-#[cfg(feature = "wallet")]
-use cdk::wallet::SendOptions;
+use cdk::wallet::{RecoveryReport, SendOptions, Wallet};
 use cdk::Amount;
 use rand::random;
 use tokio::time::sleep;
@@ -76,6 +74,13 @@ async fn main() {
 
         let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None).unwrap();
 
+        // Required: Recover from interrupted operations (swap, send, receive, melt)
+        let recovery: RecoveryReport = wallet.recover_incomplete_sagas().await.unwrap();
+        println!("Recovered {} operations", recovery.recovered);
+
+        // Optional: Check and mint pending mint quotes (requires network)
+        let _minted = wallet.mint_unissued_quotes().await.unwrap();
+
         let quote = wallet.mint_quote(PaymentMethod::BOLT11, Some(amount), None, None).await.unwrap();
 
         println!("Pay request: {}", quote.request);

+ 14 - 1
crates/cdk/examples/wallet.rs

@@ -5,7 +5,7 @@ use std::time::Duration;
 
 use cdk::nuts::nut00::ProofsMethods;
 use cdk::nuts::{CurrencyUnit, PaymentMethod};
-use cdk::wallet::{SendOptions, Wallet};
+use cdk::wallet::{RecoveryReport, SendOptions, Wallet};
 use cdk::Amount;
 use cdk_sqlite::wallet::memory;
 use rand::random;
@@ -26,6 +26,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     // Create a new wallet
     let wallet = Wallet::new(mint_url, unit, localstore, seed, None)?;
 
+    // Recover from incomplete operations (required after wallet creation)
+    let recovery: RecoveryReport = wallet.recover_incomplete_sagas().await?;
+    println!(
+        "Recovered {} operations, {} compensated, {} skipped, {} failed",
+        recovery.recovered, recovery.compensated, recovery.skipped, recovery.failed
+    );
+
+    // Check and mint pending mint quotes (optional, requires network)
+    let minted = wallet.mint_unissued_quotes().await?;
+    if minted > Amount::ZERO {
+        println!("Minted {} from pending quotes", minted);
+    }
+
     let quote = wallet
         .mint_quote(PaymentMethod::BOLT11, Some(amount), None, None)
         .await?;

+ 23 - 11
crates/cdk/src/wallet/README.md

@@ -5,22 +5,34 @@ The CDK [`Wallet`] is a high level Cashu wallet. The [`Wallet`] is for a single
 
 ## Example
 
-### Create [`Wallet`]
+### Create and Initialize [`Wallet`]
+
 ```rust
-  use std::sync::Arc;
-  use cdk::nuts::CurrencyUnit;
-  use cdk::wallet::Wallet;
-  use cdk_sqlite::wallet::memory;
-  use rand::random;
-
-  #[tokio::main]
-  async fn main() -> anyhow::Result<()> {
+use std::sync::Arc;
+use cdk::nuts::CurrencyUnit;
+use cdk::wallet::Wallet;
+use cdk_sqlite::wallet::memory;
+use rand::random;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
     let seed = random::<[u8; 64]>();
     let mint_url = "https://fake.thesimplekid.dev";
     let unit = CurrencyUnit::Sat;
 
     let localstore = memory::empty().await?;
-    let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None);
+    let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None)?;
+
+    // Required: Recover crashed operations (swap, send, receive, melt)
+    // This prevents proofs from being stuck in reserved states.
+    let report = wallet.recover_incomplete_sagas().await?;
+    println!("Recovered: {}, Compensated: {}, Skipped: {}, Failed: {}",
+        report.recovered, report.compensated, report.skipped, report.failed);
+
+    // Optional: Check and mint pending mint quotes (makes network calls)
+    let minted = wallet.mint_unissued_quotes().await?;
+    println!("Minted {} from pending quotes", minted);
+
     Ok(())
-  }
+}
 ```

+ 6 - 0
crates/cdk/src/wallet/issue/mod.rs

@@ -270,6 +270,12 @@ impl Wallet {
 
     /// Refresh states and mint all unissued quotes that have mintable amounts.
     /// Returns the total amount minted across all quotes.
+    ///
+    /// # Privacy
+    ///
+    /// This method retrieves all unissued mint quotes from the local store and
+    /// checks their state with the mint. This has a negative privacy effect of
+    /// linking all these quotes to a single wallet session.
     #[instrument(skip(self))]
     pub async fn mint_unissued_quotes(&self) -> Result<Amount, Error> {
         let mint_quotes = self.localstore.get_unissued_mint_quotes().await?;

+ 9 - 0
crates/cdk/src/wallet/mod.rs

@@ -92,6 +92,15 @@ use crate::nuts::nut00::ProofsMethods;
 /// The CDK [`Wallet`] is a high level cashu wallet.
 ///
 /// A [`Wallet`] is for a single mint and single unit.
+///
+/// # Initialization
+///
+/// After creating a wallet, call [`Wallet::recover_incomplete_sagas`] to recover
+/// from interrupted operations (swap, send, receive, melt). This is required to
+/// prevent proofs from being stuck in reserved states after a crash.
+///
+/// For pending mint quotes, call [`Wallet::mint_unissued_quotes`] which checks
+/// quote states with the mint and mints available tokens. This makes network calls.
 #[derive(Debug, Clone)]
 pub struct Wallet {
     /// Mint Url

+ 8 - 12
crates/cdk/src/wallet/recovery.rs

@@ -36,16 +36,16 @@ struct OutputRecoveryParams<'a> {
     counter_end: u32,
 }
 
-/// Report of recovery operations performed
+/// Report of recovery operations performed by [`Wallet::recover_incomplete_sagas`].
 #[derive(Debug, Default)]
 pub struct RecoveryReport {
-    /// Number of sagas that were successfully recovered
+    /// Operations successfully completed after crash
     pub recovered: usize,
-    /// Number of sagas that were compensated (rolled back)
+    /// Operations rolled back (resources released)
     pub compensated: usize,
-    /// Number of sagas that were skipped (e.g., pending external state)
+    /// Operations still pending (will retry on next call)
     pub skipped: usize,
-    /// Number of sagas that failed to recover
+    /// Operations that could not be recovered
     pub failed: usize,
 }
 
@@ -274,14 +274,10 @@ impl RecoveryHelpers for Wallet {
 }
 
 impl Wallet {
-    /// Recover from incomplete sagas.
+    /// Recover from incomplete operations after a crash. Call on startup.
     ///
-    /// This method should be called on wallet initialization to recover from
-    /// any incomplete operations that were interrupted by a crash.
-    ///
-    /// # Returns
-    ///
-    /// A report of the recovery operations performed.
+    /// Handles interrupted swap, send, receive, and melt operations to prevent
+    /// proofs from being stuck in reserved states. Requires network connection.
     #[instrument(skip(self))]
     pub async fn recover_incomplete_sagas(&self) -> Result<RecoveryReport, Error> {
         self.cleanup_orphaned_quote_reservations().await?;