From 303204d8585b223b21464720564f23e5cba515ea Mon Sep 17 00:00:00 2001 From: David Caseria Date: Mon, 13 May 2024 09:46:32 -0400 Subject: [PATCH] Add wallet tracing and fix receive bug Co-authored-by: thesimplekid --- Cargo.toml | 2 +- crates/cdk-redb/src/wallet.rs | 23 ++++++++++++++++++++++ crates/cdk/Cargo.toml | 30 ++++++++++++++++++++-------- crates/cdk/src/client.rs | 14 +++++++++++++ crates/cdk/src/wallet.rs | 37 +++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e4553072..13d438eb 100644 --- a/Cargo.toml +++ b/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 } diff --git a/crates/cdk-redb/src/wallet.rs b/crates/cdk-redb/src/wallet.rs index 07d1f421..600ecee5 100644 --- a/crates/cdk-redb/src/wallet.rs +++ b/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, Self::Err> { let db = self.db.lock().await; let read_txn = db.begin_read().map_err(Into::::into)?; @@ -119,6 +122,7 @@ impl WalletDatabase for RedbWalletDatabase { Ok(None) } + #[instrument(skip(self))] async fn get_mints(&self) -> Result>, 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, Self::Err> { let db = self.db.lock().await; let read_txn = db.begin_read().map_err(Into::::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, 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, 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, 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, Self::Err> { let db = self.db.lock().await; let read_txn = db.begin_read().map_err(Error::from)?; diff --git a/crates/cdk/Cargo.toml b/crates/cdk/Cargo.toml index c3d068ab..fd31c5e9 100644 --- a/crates/cdk/Cargo.toml +++ b/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"] } diff --git a/crates/cdk/src/client.rs b/crates/cdk/src/client.rs index d970e2f1..cd309f3f 100644 --- a/crates/cdk/src/client.rs +++ b/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, Error> { let url = join_url(mint_url, &["v1", "keys"])?; let keys = self.inner.get(url).send().await?.json::().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 { 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 { let url = join_url(mint_url, &["v1", "keysets"])?; let res = self.inner.get(url).send().await?.json::().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 { 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, diff --git a/crates/cdk/src/wallet.rs b/crates/cdk/src/wallet.rs index 329749e7..a2a5b9fa 100644 --- a/crates/cdk/src/wallet.rs +++ b/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 { 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, 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, 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, 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 { // 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 { 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 { // 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 = HashMap::new();