diff --git a/crates/cdk-cli/src/main.rs b/crates/cdk-cli/src/main.rs index bad6212e..91f18084 100644 --- a/crates/cdk-cli/src/main.rs +++ b/crates/cdk-cli/src/main.rs @@ -17,6 +17,7 @@ use tracing::Level; use tracing_subscriber::EnvFilter; use url::Url; +mod nostr_storage; mod sub_commands; const DEFAULT_WORK_DIR: &str = ".cdk-cli"; @@ -188,6 +189,7 @@ async fn main() -> Result<()> { localstore, &mnemonic.to_seed_normalized(""), sub_command_args, + &work_dir, ) .await } diff --git a/crates/cdk-cli/src/nostr_storage.rs b/crates/cdk-cli/src/nostr_storage.rs new file mode 100644 index 00000000..475d2804 --- /dev/null +++ b/crates/cdk-cli/src/nostr_storage.rs @@ -0,0 +1,37 @@ +use std::fs; +use std::path::Path; + +use anyhow::Result; +use cdk::nuts::PublicKey; +use cdk::util::hex; + +/// Stores the last checked time for a nostr key in a file +pub async fn store_nostr_last_checked( + work_dir: &Path, + verifying_key: &PublicKey, + last_checked: u32, +) -> Result<()> { + let key_hex = hex::encode(verifying_key.to_bytes()); + let file_path = work_dir.join(format!("nostr_last_checked_{}", key_hex)); + + fs::write(file_path, last_checked.to_string())?; + + Ok(()) +} + +/// Gets the last checked time for a nostr key from a file +pub async fn get_nostr_last_checked( + work_dir: &Path, + verifying_key: &PublicKey, +) -> Result> { + let key_hex = hex::encode(verifying_key.to_bytes()); + let file_path = work_dir.join(format!("nostr_last_checked_{}", key_hex)); + + match fs::read_to_string(file_path) { + Ok(content) => { + let timestamp = content.trim().parse::()?; + Ok(Some(timestamp)) + } + Err(_) => Ok(None), + } +} diff --git a/crates/cdk-cli/src/sub_commands/receive.rs b/crates/cdk-cli/src/sub_commands/receive.rs index 990ca401..54b21205 100644 --- a/crates/cdk-cli/src/sub_commands/receive.rs +++ b/crates/cdk-cli/src/sub_commands/receive.rs @@ -1,4 +1,5 @@ use std::collections::HashSet; +use std::path::Path; use std::str::FromStr; use std::sync::Arc; @@ -14,6 +15,8 @@ use clap::Args; use nostr_sdk::nips::nip04; use nostr_sdk::{Filter, Keys, Kind, Timestamp}; +use crate::nostr_storage; + #[derive(Args)] pub struct ReceiveSubCommand { /// Cashu Token @@ -40,6 +43,7 @@ pub async fn receive( localstore: Arc + Send + Sync>, seed: &[u8], sub_command_args: &ReceiveSubCommand, + work_dir: &Path, ) -> Result<()> { let mut signing_keys = Vec::new(); @@ -89,12 +93,19 @@ pub async fn receive( signing_keys.push(nostr_key.clone()); let relays = sub_command_args.relay.clone(); - let since = localstore - .get_nostr_last_checked(&nostr_key.public_key()) - .await?; + let since = + nostr_storage::get_nostr_last_checked(work_dir, &nostr_key.public_key()).await?; let tokens = nostr_receive(relays, nostr_key.clone(), since).await?; + // Store the current time as last checked + nostr_storage::store_nostr_last_checked( + work_dir, + &nostr_key.public_key(), + unix_time() as u32, + ) + .await?; + let mut total_amount = Amount::ZERO; for token_str in &tokens { match receive_token( @@ -116,9 +127,6 @@ pub async fn receive( } } - localstore - .add_nostr_last_checked(nostr_key.public_key(), unix_time() as u32) - .await?; total_amount } }; diff --git a/crates/cdk-common/src/database/wallet.rs b/crates/cdk-common/src/database/wallet.rs index e9213a1e..f3662542 100644 --- a/crates/cdk-common/src/database/wallet.rs +++ b/crates/cdk-common/src/database/wallet.rs @@ -105,16 +105,4 @@ pub trait Database: Debug { async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>; /// Get current Keyset counter async fn get_keyset_counter(&self, keyset_id: &Id) -> Result, Self::Err>; - - /// Get when nostr key was last checked - async fn get_nostr_last_checked( - &self, - verifying_key: &PublicKey, - ) -> Result, Self::Err>; - /// Update last checked time - async fn add_nostr_last_checked( - &self, - verifying_key: PublicKey, - last_checked: u32, - ) -> Result<(), Self::Err>; } diff --git a/crates/cdk-redb/src/wallet/mod.rs b/crates/cdk-redb/src/wallet/mod.rs index ff7a8478..8f608746 100644 --- a/crates/cdk-redb/src/wallet/mod.rs +++ b/crates/cdk-redb/src/wallet/mod.rs @@ -702,42 +702,4 @@ impl WalletDatabase for WalletRedbDatabase { Ok(counter.map(|c| c.value())) } - - #[instrument(skip_all)] - async fn get_nostr_last_checked( - &self, - verifying_key: &PublicKey, - ) -> Result, Self::Err> { - let read_txn = self.db.begin_read().map_err(Error::from)?; - let table = read_txn - .open_table(NOSTR_LAST_CHECKED) - .map_err(Error::from)?; - - let last_checked = table - .get(verifying_key.to_string().as_str()) - .map_err(Error::from)?; - - Ok(last_checked.map(|c| c.value())) - } - - #[instrument(skip(self, verifying_key))] - async fn add_nostr_last_checked( - &self, - verifying_key: PublicKey, - last_checked: u32, - ) -> Result<(), Self::Err> { - let write_txn = self.db.begin_write().map_err(Error::from)?; - { - let mut table = write_txn - .open_table(NOSTR_LAST_CHECKED) - .map_err(Error::from)?; - - table - .insert(verifying_key.to_string().as_str(), last_checked) - .map_err(Error::from)?; - } - write_txn.commit().map_err(Error::from)?; - - Ok(()) - } } diff --git a/crates/cdk-sqlite/src/wallet/mod.rs b/crates/cdk-sqlite/src/wallet/mod.rs index 3f842b23..348ebe7d 100644 --- a/crates/cdk-sqlite/src/wallet/mod.rs +++ b/crates/cdk-sqlite/src/wallet/mod.rs @@ -782,61 +782,6 @@ WHERE id=?; Ok(count) } - - #[instrument(skip_all)] - async fn get_nostr_last_checked( - &self, - verifying_key: &PublicKey, - ) -> Result, Self::Err> { - let rec = sqlx::query( - r#" -SELECT last_check -FROM nostr_last_checked -WHERE key=?; - "#, - ) - .bind(verifying_key.to_bytes().to_vec()) - .fetch_one(&self.pool) - .await; - - let count = match rec { - Ok(rec) => { - let count: Option = rec.try_get("last_check").map_err(Error::from)?; - count - } - Err(err) => match err { - sqlx::Error::RowNotFound => return Ok(None), - _ => return Err(Error::SQLX(err).into()), - }, - }; - - Ok(count) - } - - #[instrument(skip_all)] - async fn add_nostr_last_checked( - &self, - verifying_key: PublicKey, - last_checked: u32, - ) -> Result<(), Self::Err> { - sqlx::query( - r#" -INSERT INTO nostr_last_checked -(key, last_check) -VALUES (?, ?) -ON CONFLICT(key) DO UPDATE SET - last_check = excluded.last_check -; - "#, - ) - .bind(verifying_key.to_bytes().to_vec()) - .bind(last_checked) - .execute(&self.pool) - .await - .map_err(Error::from)?; - - Ok(()) - } } fn sqlite_row_to_mint_info(row: &SqliteRow) -> Result { @@ -970,11 +915,13 @@ fn sqlite_row_to_proof_info(row: &SqliteRow) -> Result { #[cfg(test)] mod tests { - use std::env::temp_dir; #[tokio::test] #[cfg(feature = "sqlcipher")] async fn test_sqlcipher() { + use cdk_common::mint_url::MintUrl; + use cdk_common::MintInfo; + use super::*; let path = std::env::temp_dir() .to_path_buf() @@ -985,14 +932,15 @@ mod tests { db.migrate().await; - // do something simple to test the database - let pk = PublicKey::from_hex( - "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104", - ) - .unwrap(); - let last_checked = 6969; - db.add_nostr_last_checked(pk, last_checked).await.unwrap(); - let res = db.get_nostr_last_checked(&pk).await.unwrap(); - assert_eq!(res, Some(last_checked)); + let mint_info = MintInfo::new().description("test"); + let mint_url = MintUrl::from_str("https://mint.xyz").unwrap(); + + db.add_mint(mint_url.clone(), Some(mint_info.clone())) + .await + .unwrap(); + + let res = db.get_mint(mint_url).await.unwrap(); + assert_eq!(mint_info, res.clone().unwrap()); + assert_eq!("test", &res.unwrap().description.unwrap()); } }