Add wallet tracing and fix receive bug

Co-authored-by: thesimplekid <tsk@thesimplekid.com>
This commit is contained in:
David Caseria
2024-05-13 09:46:32 -04:00
committed by thesimplekid
parent a9f0116836
commit 303204d858
5 changed files with 97 additions and 9 deletions

View File

@@ -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 }

View File

@@ -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)?;

View File

@@ -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"] }

View File

@@ -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,

View File

@@ -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();