Răsfoiți Sursa

Add wallet tracing and fix receive bug

Co-authored-by: thesimplekid <tsk@thesimplekid.com>
David Caseria 11 luni în urmă
părinte
comite
303204d858
5 a modificat fișierele cu 97 adăugiri și 9 ștergeri
  1. 1 1
      Cargo.toml
  2. 23 0
      crates/cdk-redb/src/wallet.rs
  3. 22 8
      crates/cdk/Cargo.toml
  4. 14 0
      crates/cdk/src/client.rs
  5. 37 0
      crates/cdk/src/wallet.rs

+ 1 - 1
Cargo.toml

@@ -28,7 +28,7 @@ cdk = { path = "./crates/cdk", default-features = false }
 cdk-rexie = { path = "./crates/cdk-rexie", default-features = false }
 tokio = { version = "1.32", default-features = false }
 thiserror = "1"
-tracing = { version = "0.1", default-features = false }
+tracing = { version = "0.1", default-features = false, features = ["attributes"] }
 serde = { version = "1", default-features = false, features = ["derive"] }
 serde_json = "1"
 serde-wasm-bindgen = { version = "0.6.5", default-features = false }

+ 23 - 0
crates/cdk-redb/src/wallet.rs

@@ -10,6 +10,7 @@ use cdk::types::{MeltQuote, MintQuote};
 use cdk::url::UncheckedUrl;
 use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
 use tokio::sync::Mutex;
+use tracing::instrument;
 
 use super::error::Error;
 
@@ -79,6 +80,7 @@ impl RedbWalletDatabase {
 impl WalletDatabase for RedbWalletDatabase {
     type Err = cdk_database::Error;
 
+    #[instrument(skip(self))]
     async fn add_mint(
         &self,
         mint_url: UncheckedUrl,
@@ -104,6 +106,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self))]
     async fn get_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Self::Err> {
         let db = self.db.lock().await;
         let read_txn = db.begin_read().map_err(Into::<Error>::into)?;
@@ -119,6 +122,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(None)
     }
 
+    #[instrument(skip(self))]
     async fn get_mints(&self) -> Result<HashMap<UncheckedUrl, Option<MintInfo>>, Self::Err> {
         let db = self.db.lock().await;
         let read_txn = db.begin_read().map_err(Error::from)?;
@@ -138,6 +142,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(mints)
     }
 
+    #[instrument(skip(self))]
     async fn add_mint_keysets(
         &self,
         mint_url: UncheckedUrl,
@@ -168,6 +173,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self))]
     async fn get_mint_keysets(
         &self,
         mint_url: UncheckedUrl,
@@ -188,6 +194,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(keysets)
     }
 
+    #[instrument(skip_all)]
     async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
         let write_txn = db.begin_write().map_err(Error::from)?;
@@ -209,6 +216,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip_all)]
     async fn get_mint_quote(&self, quote_id: &str) -> Result<Option<MintQuote>, Self::Err> {
         let db = self.db.lock().await;
         let read_txn = db.begin_read().map_err(Into::<Error>::into)?;
@@ -223,6 +231,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(None)
     }
 
+    #[instrument(skip_all)]
     async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
         let write_txn = db.begin_write().map_err(Error::from)?;
@@ -239,6 +248,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip_all)]
     async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
         let write_txn = db.begin_write().map_err(Error::from)?;
@@ -260,6 +270,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip_all)]
     async fn get_melt_quote(&self, quote_id: &str) -> Result<Option<MeltQuote>, Self::Err> {
         let db = self.db.lock().await;
         let read_txn = db.begin_read().map_err(Error::from)?;
@@ -274,6 +285,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(None)
     }
 
+    #[instrument(skip_all)]
     async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
         let write_txn = db.begin_write().map_err(Error::from)?;
@@ -290,6 +302,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip_all)]
     async fn add_keys(&self, keys: Keys) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
         let write_txn = db.begin_write().map_err(Error::from)?;
@@ -309,6 +322,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self))]
     async fn get_keys(&self, id: &Id) -> Result<Option<Keys>, Self::Err> {
         let db = self.db.lock().await;
         let read_txn = db.begin_read().map_err(Error::from)?;
@@ -321,6 +335,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(None)
     }
 
+    #[instrument(skip(self))]
     async fn remove_keys(&self, id: &Id) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
         let write_txn = db.begin_write().map_err(Error::from)?;
@@ -336,6 +351,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self, proofs))]
     async fn add_proofs(&self, mint_url: UncheckedUrl, proofs: Proofs) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
 
@@ -360,6 +376,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self))]
     async fn get_proofs(&self, mint_url: UncheckedUrl) -> Result<Option<Proofs>, Self::Err> {
         let db = self.db.lock().await;
         let read_txn = db.begin_read().map_err(Error::from)?;
@@ -377,6 +394,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(proofs)
     }
 
+    #[instrument(skip(self, proofs))]
     async fn remove_proofs(
         &self,
         mint_url: UncheckedUrl,
@@ -405,6 +423,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self, proofs))]
     async fn add_pending_proofs(
         &self,
         mint_url: UncheckedUrl,
@@ -433,6 +452,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self))]
     async fn get_pending_proofs(
         &self,
         mint_url: UncheckedUrl,
@@ -453,6 +473,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(proofs)
     }
 
+    #[instrument(skip(self, proofs))]
     async fn remove_pending_proofs(
         &self,
         mint_url: UncheckedUrl,
@@ -481,6 +502,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self))]
     async fn increment_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
 
@@ -512,6 +534,7 @@ impl WalletDatabase for RedbWalletDatabase {
         Ok(())
     }
 
+    #[instrument(skip(self))]
     async fn get_keyset_counter(&self, keyset_id: &Id) -> Result<Option<u64>, Self::Err> {
         let db = self.db.lock().await;
         let read_txn = db.begin_read().map_err(Error::from)?;

+ 22 - 8
crates/cdk/Cargo.toml

@@ -21,25 +21,39 @@ nut13 = ["dep:bip39"]
 async-trait = "0.1"
 base64 = "0.22" # bitcoin uses v0.13 (optional dep)
 bip39 = { version = "2.0", optional = true }
-bitcoin = { version = "0.30", features = ["serde", "rand", "rand-std"] } # lightning-invoice uses v0.30
+bitcoin = { version = "0.30", features = [
+    "serde",
+    "rand",
+    "rand-std",
+] } # lightning-invoice uses v0.30
 http = "1.0"
 lightning-invoice = { version = "0.30", features = ["serde"] }
 once_cell = "1.19"
-reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "socks"], optional = true }
-serde = { version = "1.0", default-features = false, features = ["derive"]}
+reqwest = { version = "0.12", default-features = false, features = [
+    "json",
+    "rustls-tls",
+    "socks",
+], optional = true }
+serde = { version = "1.0", default-features = false, features = ["derive"] }
 serde_json = "1.0"
 serde_with = "3.4"
-tracing = { version = "0.1", default-features = false }
+tracing = { version = "0.1", default-features = false, features = [
+    "attributes",
+    "log",
+] }
 thiserror = "1.0"
 url = "2.3"
 uuid = { version = "1.6", features = ["v4"] }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-tokio = { workspace = true, features = ["rt-multi-thread", "time", "macros", "sync"] }
+tokio = { workspace = true, features = [
+    "rt-multi-thread",
+    "time",
+    "macros",
+    "sync",
+] }
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
 tokio = { workspace = true, features = ["rt", "macros", "sync", "time"] }
 getrandom = { version = "0.2", features = ["js"] }
-instant = { version = "0.1", features = [ "wasm-bindgen", "inaccurate" ] }
-
-
+instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] }

+ 14 - 0
crates/cdk/src/client.rs

@@ -3,6 +3,7 @@
 use reqwest::Client;
 use serde_json::Value;
 use thiserror::Error;
+use tracing::instrument;
 use url::Url;
 
 use crate::error::ErrorResponse;
@@ -74,6 +75,7 @@ impl HttpClient {
     }
 
     /// Get Active Mint Keys [NUT-01]
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_mint_keys(&self, mint_url: Url) -> Result<Vec<KeySet>, Error> {
         let url = join_url(mint_url, &["v1", "keys"])?;
         let keys = self.inner.get(url).send().await?.json::<Value>().await?;
@@ -83,6 +85,7 @@ impl HttpClient {
     }
 
     /// Get Keyset Keys [NUT-01]
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_mint_keyset(&self, mint_url: Url, keyset_id: Id) -> Result<KeySet, Error> {
         let url = join_url(mint_url, &["v1", "keys", &keyset_id.to_string()])?;
         let keys = self
@@ -99,6 +102,7 @@ impl HttpClient {
     }
 
     /// Get Keysets [NUT-02]
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_mint_keysets(&self, mint_url: Url) -> Result<KeysetResponse, Error> {
         let url = join_url(mint_url, &["v1", "keysets"])?;
         let res = self.inner.get(url).send().await?.json::<Value>().await?;
@@ -113,6 +117,7 @@ impl HttpClient {
     }
 
     /// Mint Quote [NUT-04]
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn post_mint_quote(
         &self,
         mint_url: Url,
@@ -137,6 +142,7 @@ impl HttpClient {
     }
 
     /// Mint Quote status
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_mint_quote_status(
         &self,
         mint_url: Url,
@@ -158,6 +164,7 @@ impl HttpClient {
     }
 
     /// Mint Tokens [NUT-04]
+    #[instrument(skip(self, quote, premint_secrets), fields(mint_url = %mint_url))]
     pub async fn post_mint(
         &self,
         mint_url: Url,
@@ -190,6 +197,7 @@ impl HttpClient {
     }
 
     /// Melt Quote [NUT-05]
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn post_melt_quote(
         &self,
         mint_url: Url,
@@ -214,6 +222,7 @@ impl HttpClient {
     }
 
     /// Melt Quote Status
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_melt_quote_status(
         &self,
         mint_url: Url,
@@ -236,6 +245,7 @@ impl HttpClient {
 
     /// Melt [NUT-05]
     /// [Nut-08] Lightning fee return if outputs defined
+    #[instrument(skip(self, quote, inputs, outputs), fields(mint_url = %mint_url))]
     pub async fn post_melt(
         &self,
         mint_url: Url,
@@ -264,6 +274,7 @@ impl HttpClient {
     }
 
     /// Split Token [NUT-06]
+    #[instrument(skip(self, swap_request), fields(mint_url = %mint_url))]
     pub async fn post_swap(
         &self,
         mint_url: Url,
@@ -283,6 +294,7 @@ impl HttpClient {
     }
 
     /// Get Mint Info [NUT-06]
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error> {
         let url = join_url(mint_url, &["v1", "info"])?;
 
@@ -297,6 +309,7 @@ impl HttpClient {
     }
 
     /// Spendable check [NUT-07]
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn post_check_state(
         &self,
         mint_url: Url,
@@ -323,6 +336,7 @@ impl HttpClient {
         }
     }
 
+    #[instrument(skip(self, request), fields(mint_url = %mint_url))]
     pub async fn post_restore(
         &self,
         mint_url: Url,

+ 37 - 0
crates/cdk/src/wallet.rs

@@ -9,6 +9,7 @@ use bip39::Mnemonic;
 use bitcoin::hashes::sha256::Hash as Sha256Hash;
 use bitcoin::hashes::Hash;
 use thiserror::Error;
+use tracing::instrument;
 
 use crate::cdk_database::wallet_memory::WalletMemoryDatabase;
 use crate::cdk_database::{self, WalletDatabase};
@@ -126,6 +127,7 @@ impl Wallet {
     }
 
     /// Total Balance of wallet
+    #[instrument(skip(self))]
     pub async fn total_balance(&self) -> Result<Amount, Error> {
         let mints = self.localstore.get_mints().await?;
         let mut balance = Amount::ZERO;
@@ -141,6 +143,7 @@ impl Wallet {
         Ok(balance)
     }
 
+    #[instrument(skip(self))]
     pub async fn mint_balances(&self) -> Result<HashMap<UncheckedUrl, Amount>, Error> {
         let mints = self.localstore.get_mints().await?;
 
@@ -159,10 +162,12 @@ impl Wallet {
         Ok(balances)
     }
 
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_proofs(&self, mint_url: UncheckedUrl) -> Result<Option<Proofs>, Error> {
         Ok(self.localstore.get_proofs(mint_url).await?)
     }
 
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn add_mint(&self, mint_url: UncheckedUrl) -> Result<Option<MintInfo>, Error> {
         let mint_info = match self
             .client
@@ -183,6 +188,7 @@ impl Wallet {
         Ok(mint_info)
     }
 
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_keyset_keys(
         &self,
         mint_url: &UncheckedUrl,
@@ -204,6 +210,7 @@ impl Wallet {
         Ok(keys)
     }
 
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_mint_keysets(
         &self,
         mint_url: &UncheckedUrl,
@@ -218,6 +225,7 @@ impl Wallet {
     }
 
     /// Get active mint keyset
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_active_mint_keys(
         &self,
         mint_url: &UncheckedUrl,
@@ -238,6 +246,7 @@ impl Wallet {
     }
 
     /// Refresh Mint keys
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn refresh_mint_keys(&self, mint_url: &UncheckedUrl) -> Result<(), Error> {
         let current_mint_keysets_info = self
             .client
@@ -276,6 +285,7 @@ impl Wallet {
     }
 
     /// Check if a proof is spent
+    #[instrument(skip(self, proofs), fields(mint_url = %mint_url))]
     pub async fn check_proofs_spent(
         &self,
         mint_url: UncheckedUrl,
@@ -297,6 +307,7 @@ impl Wallet {
     }
 
     /// Mint Quote
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn mint_quote(
         &mut self,
         mint_url: UncheckedUrl,
@@ -323,6 +334,7 @@ impl Wallet {
     }
 
     /// Mint quote status
+    #[instrument(skip(self, quote_id), fields(mint_url = %mint_url))]
     pub async fn mint_quote_status(
         &self,
         mint_url: UncheckedUrl,
@@ -348,6 +360,7 @@ impl Wallet {
         Ok(response)
     }
 
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     async fn active_mint_keyset(
         &mut self,
         mint_url: &UncheckedUrl,
@@ -378,6 +391,7 @@ impl Wallet {
         Err(Error::NoActiveKeyset)
     }
 
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     async fn active_keys(
         &mut self,
         mint_url: &UncheckedUrl,
@@ -403,6 +417,7 @@ impl Wallet {
     }
 
     /// Mint
+    #[instrument(skip(self, quote_id), fields(mint_url = %mint_url))]
     pub async fn mint(&mut self, mint_url: UncheckedUrl, quote_id: &str) -> Result<Amount, Error> {
         // Check that mint is in store of mints
         if self.localstore.get_mint(mint_url.clone()).await?.is_none() {
@@ -510,6 +525,7 @@ impl Wallet {
     }
 
     /// Swap
+    #[instrument(skip(self, input_proofs), fields(mint_url = %mint_url))]
     pub async fn swap(
         &mut self,
         mint_url: &UncheckedUrl,
@@ -617,6 +633,7 @@ impl Wallet {
     }
 
     /// Create Swap Payload
+    #[instrument(skip(self, proofs), fields(mint_url = %mint_url))]
     async fn create_swap(
         &mut self,
         mint_url: &UncheckedUrl,
@@ -747,6 +764,7 @@ impl Wallet {
     }
 
     /// Send
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn send(
         &mut self,
         mint_url: &UncheckedUrl,
@@ -783,6 +801,7 @@ impl Wallet {
     }
 
     /// Melt Quote
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn melt_quote(
         &mut self,
         mint_url: UncheckedUrl,
@@ -814,6 +833,7 @@ impl Wallet {
     }
 
     /// Melt quote status
+    #[instrument(skip(self, quote_id), fields(mint_url = %mint_url))]
     pub async fn melt_quote_status(
         &self,
         mint_url: UncheckedUrl,
@@ -840,6 +860,7 @@ impl Wallet {
     }
 
     // Select proofs
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn select_proofs(
         &self,
         mint_url: UncheckedUrl,
@@ -900,6 +921,7 @@ impl Wallet {
     }
 
     /// Melt
+    #[instrument(skip(self, quote_id), fields(mint_url = %mint_url))]
     pub async fn melt(&mut self, mint_url: &UncheckedUrl, quote_id: &str) -> Result<Melted, Error> {
         let quote_info = self.localstore.get_melt_quote(quote_id).await?;
 
@@ -1016,6 +1038,7 @@ impl Wallet {
     }
 
     /// Receive
+    #[instrument(skip_all)]
     pub async fn receive(
         &mut self,
         encoded_token: &str,
@@ -1032,6 +1055,16 @@ impl Wallet {
                 continue;
             }
 
+            // Add mint if it does not exist in the store
+            if self
+                .localstore
+                .get_mint(token.mint.clone())
+                .await?
+                .is_none()
+            {
+                self.add_mint(token.mint.clone()).await?;
+            }
+
             let active_keyset_id = self.active_mint_keyset(&token.mint, &unit).await?;
 
             let keys = self.get_keyset_keys(&token.mint, active_keyset_id).await?;
@@ -1150,6 +1183,7 @@ impl Wallet {
         Ok(())
     }
 
+    #[instrument(skip(self, proofs), fields(mint_url = %mint_url))]
     pub fn proofs_to_token(
         &self,
         mint_url: UncheckedUrl,
@@ -1161,6 +1195,7 @@ impl Wallet {
     }
 
     #[cfg(feature = "nut13")]
+    #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn restore(&mut self, mint_url: UncheckedUrl) -> Result<Amount, Error> {
         // Check that mint is in store of mints
         if self.localstore.get_mint(mint_url.clone()).await?.is_none() {
@@ -1264,6 +1299,7 @@ impl Wallet {
     /// Verify all proofs in token have meet the required spend
     /// Can be used to allow a wallet to accept payments offline while reducing
     /// the risk of claiming back to the limits let by the spending_conditions
+    #[instrument(skip(self, token))]
     pub fn verify_token_p2pk(
         &self,
         token: &Token,
@@ -1381,6 +1417,7 @@ impl Wallet {
     }
 
     /// Verify all proofs in token have a valid DLEQ proof
+    #[instrument(skip(self, token))]
     pub async fn verify_token_dleq(&self, token: &Token) -> Result<(), Error> {
         let mut keys_cache: HashMap<Id, Keys> = HashMap::new();