diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e2cd92e..ecfff9cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,7 @@ jobs: -p cdk --no-default-features, -p cdk --no-default-features --features wallet, -p cdk --no-default-features --features mint, + -p cdk --no-default-features --features wallet --features nostr, -p cdk-redb ] steps: @@ -64,6 +65,7 @@ jobs: -p cdk, -p cdk --no-default-features, -p cdk --no-default-features --features wallet, + -p cdk --no-default-features --features wallet --features nostr, -p cdk-js ] steps: diff --git a/.helix/languages.toml b/.helix/languages.toml index 22210359..fd2475f5 100644 --- a/.helix/languages.toml +++ b/.helix/languages.toml @@ -1,2 +1,2 @@ [language-server.rust-analyzer.config] -cargo = { features = ["wallet", "mint"] } +cargo = { features = ["wallet", "mint", "nostr"] } diff --git a/crates/cdk-redb/Cargo.toml b/crates/cdk-redb/Cargo.toml index a2a88cdd..b076fb84 100644 --- a/crates/cdk-redb/Cargo.toml +++ b/crates/cdk-redb/Cargo.toml @@ -12,6 +12,7 @@ rust-version.workspace = true default = ["mint", "wallet"] mint = ["cdk/mint"] wallet = ["cdk/wallet"] +nostr = ["cdk/nostr"] [dependencies] async-trait.workspace = true diff --git a/crates/cdk-redb/src/wallet.rs b/crates/cdk-redb/src/wallet.rs index c36f0e84..2686d681 100644 --- a/crates/cdk-redb/src/wallet.rs +++ b/crates/cdk-redb/src/wallet.rs @@ -5,6 +5,8 @@ use std::sync::Arc; use async_trait::async_trait; use cdk::cdk_database; use cdk::cdk_database::WalletDatabase; +#[cfg(feature = "nostr")] +use cdk::nuts::PublicKey; use cdk::nuts::{Id, KeySetInfo, Keys, MintInfo, Proofs}; use cdk::types::{MeltQuote, MintQuote}; use cdk::url::UncheckedUrl; @@ -25,6 +27,8 @@ const PENDING_PROOFS_TABLE: MultimapTableDefinition<&str, &str> = MultimapTableDefinition::new("pending_proofs"); const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config"); const KEYSET_COUNTER: TableDefinition<&str, u32> = TableDefinition::new("keyset_counter"); +#[cfg(feature = "nostr")] +const NOSTR_LAST_CHECKED: TableDefinition<&str, u32> = TableDefinition::new("keyset_counter"); const DATABASE_VERSION: u32 = 0; @@ -64,6 +68,8 @@ impl RedbWalletDatabase { let _ = write_txn.open_table(MINT_KEYS_TABLE)?; let _ = write_txn.open_multimap_table(PROOFS_TABLE)?; let _ = write_txn.open_table(KEYSET_COUNTER)?; + #[cfg(feature = "nostr")] + let _ = write_txn.open_table(NOSTR_LAST_CHECKED)?; table.insert("db_version", "0")?; } } @@ -562,4 +568,45 @@ impl WalletDatabase for RedbWalletDatabase { Ok(counter.map(|c| c.value())) } + + #[cfg(feature = "nostr")] + #[instrument(skip(self))] + async fn get_nostr_last_checked( + &self, + verifying_key: &PublicKey, + ) -> Result, Self::Err> { + let db = self.db.lock().await; + let read_txn = 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())) + } + #[cfg(feature = "nostr")] + #[instrument(skip(self))] + async fn add_nostr_last_checked( + &self, + verifying_key: PublicKey, + last_checked: u32, + ) -> Result<(), Self::Err> { + let db = self.db.lock().await; + let write_txn = 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/Cargo.toml b/crates/cdk/Cargo.toml index 9678a066..5f45713c 100644 --- a/crates/cdk/Cargo.toml +++ b/crates/cdk/Cargo.toml @@ -13,6 +13,7 @@ license.workspace = true default = ["mint", "wallet"] mint = [] wallet = ["dep:reqwest"] +nostr = ["dep:nostr-sdk"] [dependencies] async-trait = "0.1" @@ -41,6 +42,10 @@ tracing = { version = "0.1", default-features = false, features = [ thiserror = "1" url = "2.3" uuid = { version = "1", features = ["v4"] } +nostr-sdk = { version = "0.31.0", default-features = false, features = [ + "nip04", + "nip44" +], optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { workspace = true, features = [ diff --git a/crates/cdk/src/cdk_database/mod.rs b/crates/cdk/src/cdk_database/mod.rs index 8bb0ef52..4e2b7a22 100644 --- a/crates/cdk/src/cdk_database/mod.rs +++ b/crates/cdk/src/cdk_database/mod.rs @@ -9,8 +9,10 @@ use thiserror::Error; #[cfg(feature = "mint")] use crate::mint::MintKeySetInfo; +#[cfg(any(feature = "nostr", feature = "mint"))] +use crate::nuts::PublicKey; #[cfg(feature = "mint")] -use crate::nuts::{BlindSignature, CurrencyUnit, Proof, PublicKey}; +use crate::nuts::{BlindSignature, CurrencyUnit, Proof}; #[cfg(any(feature = "wallet", feature = "mint"))] use crate::nuts::{Id, MintInfo}; #[cfg(feature = "wallet")] @@ -91,6 +93,18 @@ pub trait WalletDatabase { async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>; async fn get_keyset_counter(&self, keyset_id: &Id) -> Result, Self::Err>; + + #[cfg(feature = "nostr")] + async fn get_nostr_last_checked( + &self, + verifying_key: &PublicKey, + ) -> Result, Self::Err>; + #[cfg(feature = "nostr")] + async fn add_nostr_last_checked( + &self, + verifying_key: PublicKey, + last_checked: u32, + ) -> Result<(), Self::Err>; } #[cfg(feature = "mint")] diff --git a/crates/cdk/src/cdk_database/wallet_memory.rs b/crates/cdk/src/cdk_database/wallet_memory.rs index e5065dee..4a0a21dd 100644 --- a/crates/cdk/src/cdk_database/wallet_memory.rs +++ b/crates/cdk/src/cdk_database/wallet_memory.rs @@ -8,10 +8,13 @@ use tokio::sync::RwLock; use super::WalletDatabase; use crate::cdk_database::Error; +#[cfg(feature = "nostr")] +use crate::nuts::PublicKey; use crate::nuts::{Id, KeySetInfo, Keys, MintInfo, Proof, Proofs}; use crate::types::{MeltQuote, MintQuote}; use crate::url::UncheckedUrl; +// TODO: Change these all to RwLocks #[derive(Default, Debug, Clone)] pub struct WalletMemoryDatabase { mints: Arc>>>, @@ -22,6 +25,8 @@ pub struct WalletMemoryDatabase { proofs: Arc>>>, pending_proofs: Arc>>>, keyset_counter: Arc>>, + #[cfg(feature = "nostr")] + nostr_last_checked: Arc>>, } impl WalletMemoryDatabase { @@ -30,6 +35,7 @@ impl WalletMemoryDatabase { melt_quotes: Vec, mint_keys: Vec, keyset_counter: HashMap, + #[cfg(feature = "nostr")] nostr_last_checked: HashMap, ) -> Self { Self { mints: Arc::new(RwLock::new(HashMap::new())), @@ -46,6 +52,8 @@ impl WalletMemoryDatabase { proofs: Arc::new(RwLock::new(HashMap::new())), pending_proofs: Arc::new(RwLock::new(HashMap::new())), keyset_counter: Arc::new(RwLock::new(keyset_counter)), + #[cfg(feature = "nostr")] + nostr_last_checked: Arc::new(RwLock::new(nostr_last_checked)), } } } @@ -233,4 +241,30 @@ impl WalletDatabase for WalletMemoryDatabase { async fn get_keyset_counter(&self, id: &Id) -> Result, Error> { Ok(self.keyset_counter.read().await.get(id).cloned()) } + + #[cfg(feature = "nostr")] + async fn get_nostr_last_checked( + &self, + verifying_key: &PublicKey, + ) -> Result, Self::Err> { + Ok(self + .nostr_last_checked + .read() + .await + .get(verifying_key) + .cloned()) + } + #[cfg(feature = "nostr")] + async fn add_nostr_last_checked( + &self, + verifying_key: PublicKey, + last_checked: u32, + ) -> Result<(), Self::Err> { + self.nostr_last_checked + .write() + .await + .insert(verifying_key, last_checked); + + Ok(()) + } } diff --git a/crates/cdk/src/nuts/nut01/mod.rs b/crates/cdk/src/nuts/nut01/mod.rs index d536b3f6..16db06ba 100644 --- a/crates/cdk/src/nuts/nut01/mod.rs +++ b/crates/cdk/src/nuts/nut01/mod.rs @@ -24,6 +24,9 @@ pub enum Error { Secp256k1(#[from] secp256k1::Error), #[error(transparent)] Json(#[from] serde_json::Error), + #[cfg(feature = "nostr")] + #[error(transparent)] + NostrKey(#[from] nostr_sdk::key::Error), #[error("Invalid public key size: expected={expected}, found={found}")] InvalidPublicKeySize { expected: usize, found: usize }, } diff --git a/crates/cdk/src/nuts/nut01/public_key.rs b/crates/cdk/src/nuts/nut01/public_key.rs index dddad9d5..ad349d01 100644 --- a/crates/cdk/src/nuts/nut01/public_key.rs +++ b/crates/cdk/src/nuts/nut01/public_key.rs @@ -5,7 +5,7 @@ use core::str::FromStr; use bitcoin::hashes::sha256::Hash as Sha256Hash; use bitcoin::hashes::Hash; use bitcoin::secp256k1::schnorr::Signature; -use bitcoin::secp256k1::{self, Message}; +use bitcoin::secp256k1::{self, Message, XOnlyPublicKey}; use serde::{Deserialize, Deserializer, Serialize}; use super::Error; @@ -30,6 +30,30 @@ impl From for PublicKey { } } +#[cfg(feature = "nostr")] +impl TryFrom for nostr_sdk::PublicKey { + type Error = Error; + fn try_from(pubkey: PublicKey) -> Result { + Ok(nostr_sdk::PublicKey::from_slice(&pubkey.to_bytes())?) + } +} + +#[cfg(feature = "nostr")] +impl TryFrom for PublicKey { + type Error = Error; + fn try_from(pubkey: nostr_sdk::PublicKey) -> Result { + (&pubkey).try_into() + } +} + +#[cfg(feature = "nostr")] +impl TryFrom<&nostr_sdk::PublicKey> for PublicKey { + type Error = Error; + fn try_from(pubkey: &nostr_sdk::PublicKey) -> Result { + PublicKey::from_slice(&pubkey.to_bytes()) + } +} + impl PublicKey { /// Parse from `bytes` #[inline] @@ -70,6 +94,11 @@ impl PublicKey { self.inner.serialize_uncompressed() } + #[inline] + pub fn x_only_pubkey(&self) -> XOnlyPublicKey { + self.inner.x_only_public_key().0 + } + /// Get public key as `hex` string #[inline] pub fn to_hex(&self) -> String { diff --git a/crates/cdk/src/nuts/nut01/secret_key.rs b/crates/cdk/src/nuts/nut01/secret_key.rs index 40e912d9..b5474653 100644 --- a/crates/cdk/src/nuts/nut01/secret_key.rs +++ b/crates/cdk/src/nuts/nut01/secret_key.rs @@ -32,6 +32,22 @@ impl From for SecretKey { } } +#[cfg(feature = "nostr")] +impl TryFrom for nostr_sdk::SecretKey { + type Error = Error; + fn try_from(seckey: SecretKey) -> Result { + (&seckey).try_into() + } +} + +#[cfg(feature = "nostr")] +impl TryFrom<&SecretKey> for nostr_sdk::SecretKey { + type Error = Error; + fn try_from(seckey: &SecretKey) -> Result { + Ok(nostr_sdk::SecretKey::from_slice(&seckey.to_secret_bytes())?) + } +} + impl fmt::Display for SecretKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_secret_hex()) diff --git a/crates/cdk/src/wallet.rs b/crates/cdk/src/wallet.rs index ca7a2a54..bbbec275 100644 --- a/crates/cdk/src/wallet.rs +++ b/crates/cdk/src/wallet.rs @@ -9,6 +9,10 @@ use bitcoin::bip32::ExtendedPrivKey; use bitcoin::hashes::sha256::Hash as Sha256Hash; use bitcoin::hashes::Hash; use bitcoin::Network; +#[cfg(feature = "nostr")] +use nostr_sdk::nips::nip04; +#[cfg(feature = "nostr")] +use nostr_sdk::{Filter, NostrSigner, RelayPoolNotification, Timestamp}; use thiserror::Error; use tracing::instrument; @@ -81,6 +85,12 @@ pub enum Error { Invoice(#[from] lightning_invoice::ParseOrSemanticError), #[error(transparent)] Serde(#[from] serde_json::Error), + #[cfg(feature = "nostr")] + #[error(transparent)] + NostrClient(#[from] nostr_sdk::client::Error), + #[cfg(feature = "nostr")] + #[error(transparent)] + NostrKey(#[from] nostr_sdk::key::Error), #[error("`{0}`")] Custom(String), } @@ -96,6 +106,8 @@ pub struct Wallet { pub client: HttpClient, pub localstore: Arc + Send + Sync>, xpriv: ExtendedPrivKey, + #[cfg(feature = "nostr")] + nostr_client: nostr_sdk::Client, } impl Wallet { @@ -105,13 +117,24 @@ impl Wallet { ) -> Self { let xpriv = ExtendedPrivKey::new_master(Network::Bitcoin, seed) .expect("Could not create master key"); + Self { client: HttpClient::new(), localstore, xpriv, + #[cfg(feature = "nostr")] + nostr_client: nostr_sdk::Client::default(), } } + /// Add nostr relays to client + #[cfg(feature = "nostr")] + #[instrument(skip(self))] + pub async fn add_nostr_relays(&self, relays: Vec) -> Result<(), Error> { + self.nostr_client.add_relays(relays).await?; + Ok(()) + } + /// Total Balance of wallet #[instrument(skip(self))] pub async fn total_balance(&self) -> Result { @@ -676,7 +699,7 @@ impl Wallet { /// Create Swap Payload #[instrument(skip(self, proofs), fields(mint_url = %mint_url))] async fn create_swap( - &mut self, + &self, mint_url: &UncheckedUrl, unit: &CurrencyUnit, amount: Option, @@ -784,7 +807,7 @@ impl Wallet { }; Ok(self - .proofs_to_token( + .proof_to_token( mint_url.clone(), send_proofs.ok_or(Error::InsufficientFunds)?, memo, @@ -1003,7 +1026,7 @@ impl Wallet { /// Receive #[instrument(skip_all)] pub async fn receive( - &mut self, + &self, encoded_token: &str, signing_keys: Option>, preimages: Option>, @@ -1061,7 +1084,7 @@ impl Wallet { for proof in &mut proofs { // Verify that proof DLEQ is valid - { + if proof.dleq.is_some() { let keys = self.get_keyset_keys(&token.mint, proof.keyset_id).await?; let key = keys.amount_key(proof.amount).ok_or(Error::UnknownKey)?; proof.verify_dleq(key)?; @@ -1145,8 +1168,131 @@ impl Wallet { Ok(total_amount) } + #[cfg(feature = "nostr")] + #[instrument(skip(self))] + pub async fn nostr_receive(&self, nostr_signing_key: SecretKey) -> Result { + use nostr_sdk::{Keys, Kind, RelayMessage}; + use tokio::sync::Mutex; + + let verifying_key = nostr_signing_key.public_key(); + + let nostr_pubkey = + nostr_sdk::PublicKey::from_hex(verifying_key.x_only_pubkey().to_string())?; + + let filter = match self + .localstore + .get_nostr_last_checked(&verifying_key) + .await? + { + Some(since) => Filter::new() + .pubkey(nostr_pubkey) + .since(Timestamp::from(since as u64)), + None => Filter::new().pubkey(nostr_pubkey), + }; + + self.nostr_client.connect().await; + self.nostr_client.subscribe(vec![filter], None).await; + + let tokens: Mutex> = Mutex::new(HashSet::new()); + + // Handle subscription notifications with `handle_notifications` method + self.nostr_client + .handle_notifications(|notification| async { + let mut exit = false; + let keys = Keys::from_str(&nostr_signing_key.to_secret_hex()).unwrap(); + + match notification { + RelayPoolNotification::Event { + relay_url: _, + subscription_id: _, + event, + } => { + // Check kind + if event.kind() == Kind::EncryptedDirectMessage { + if let Ok(msg) = nip04::decrypt( + keys.secret_key()?, + event.author_ref(), + event.content(), + ) { + println!("DM: {msg}"); + + if let Some(token) = Self::token_from_text(&msg) { + tokens.lock().await.insert(token.to_string()); + } + } else { + tracing::error!("Impossible to decrypt direct message"); + } + } else { + println!("Other event: {:?}", event.kind); + } + } + RelayPoolNotification::Message { relay_url, message } => match message { + RelayMessage::Auth { challenge } => { + self.nostr_client + .set_signer(Some(NostrSigner::Keys(keys))) + .await; + let r = self.nostr_client.auth(challenge, relay_url).await?; + + tracing::debug!("Event id: {r}"); + } + RelayMessage::Notice { message } => { + tracing::debug!("Notice: {message}"); + } + RelayMessage::Ok { + event_id, + status: _, + message: _, + } => { + println!("Ok: {:?}", event_id); + } + RelayMessage::EndOfStoredEvents(_sub_id) => { + exit = true; + } + _ => { + tracing::debug!("{:?}", message); + } + }, + _ => { + tracing::debug!("{:?}", notification); + } + } + + Ok(exit) // Set to true to exit from the loop + }) + .await?; + + let mut total_received = Amount::ZERO; + for token in tokens.lock().await.iter() { + match self.receive(token, None, None).await { + Ok(amount) => total_received += amount, + Err(err) => { + tracing::error!("Could not receive token: {}", err); + } + } + } + + self.localstore + .add_nostr_last_checked(verifying_key, unix_time() as u32) + .await?; + + Ok(total_received) + } + + #[cfg(feature = "nostr")] + fn token_from_text(text: &str) -> Option<&str> { + let text = text.trim(); + if let Some(start) = text.find("cashu") { + match text[start..].find(' ') { + Some(end) => return Some(&text[start..(end + start)]), + None => return Some(&text[start..]), + } + } + + None + } + #[instrument(skip(self, proofs), fields(mint_url = %mint_url))] - pub fn proofs_to_token( + pub fn proof_to_token( &self, mint_url: UncheckedUrl, proofs: Proofs, @@ -1408,71 +1554,20 @@ impl Wallet { } } -/* +#[cfg(feature = "nostr")] #[cfg(test)] mod tests { - use std::collections::{HashMap, HashSet}; - use super::*; - use crate::client::Client; - use crate::mint::Mint; - use cashu::nuts::nut04; - #[test] - fn test_wallet() { - let mut mint = Mint::new( - "supersecretsecret", - "0/0/0/0", - HashMap::new(), - HashSet::new(), - 32, - ); + fn test_token_from_text() { + let text = " Here is some ecash: cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJhbW91bnQiOjIsInNlY3JldCI6ImI2Zjk1ODIxYmZlNjUyYjYwZGQ2ZjYwMDU4N2UyZjNhOTk4MzVhMGMyNWI4MTQzODNlYWIwY2QzOWFiNDFjNzUiLCJDIjoiMDI1YWU4ZGEyOTY2Y2E5OGVmYjA5ZDcwOGMxM2FiZmEwZDkxNGUwYTk3OTE4MmFjMzQ4MDllMjYxODY5YTBhNDJlIiwicmVzZXJ2ZWQiOmZhbHNlLCJpZCI6IjAwOWExZjI5MzI1M2U0MWUifSx7ImFtb3VudCI6Miwic2VjcmV0IjoiZjU0Y2JjNmNhZWZmYTY5MTUyOTgyM2M1MjU1MDkwYjRhMDZjNGQ3ZDRjNzNhNDFlZTFkNDBlM2ExY2EzZGZhNyIsIkMiOiIwMjMyMTIzN2JlYjcyMWU3NGI1NzcwNWE5MjJjNjUxMGQwOTYyYzAzNzlhZDM0OTJhMDYwMDliZTAyNjA5ZjA3NTAiLCJyZXNlcnZlZCI6ZmFsc2UsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSJ9LHsiYW1vdW50IjoxLCJzZWNyZXQiOiJhNzdhM2NjODY4YWM4ZGU3YmNiOWMxMzJmZWI3YzEzMDY4Nzg3ODk5Yzk3YTk2NWE2ZThkZTFiMzliMmQ2NmQ3IiwiQyI6IjAzMTY0YTMxNWVhNjM0NGE5NWI2NzM1NzBkYzg0YmZlMTQ2NDhmMTQwM2EwMDJiZmJlMDhlNWFhMWE0NDQ0YWE0MCIsInJlc2VydmVkIjpmYWxzZSwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIn1dLCJtaW50IjoiaHR0cHM6Ly90ZXN0bnV0LmNhc2h1LnNwYWNlIn1dLCJ1bml0Ijoic2F0In0= fdfdfg + sdfs"; + let token = Wallet::token_from_text(text).unwrap(); - let keys = mint.active_keyset_pubkeys(); + let token_str = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJhbW91bnQiOjIsInNlY3JldCI6ImI2Zjk1ODIxYmZlNjUyYjYwZGQ2ZjYwMDU4N2UyZjNhOTk4MzVhMGMyNWI4MTQzODNlYWIwY2QzOWFiNDFjNzUiLCJDIjoiMDI1YWU4ZGEyOTY2Y2E5OGVmYjA5ZDcwOGMxM2FiZmEwZDkxNGUwYTk3OTE4MmFjMzQ4MDllMjYxODY5YTBhNDJlIiwicmVzZXJ2ZWQiOmZhbHNlLCJpZCI6IjAwOWExZjI5MzI1M2U0MWUifSx7ImFtb3VudCI6Miwic2VjcmV0IjoiZjU0Y2JjNmNhZWZmYTY5MTUyOTgyM2M1MjU1MDkwYjRhMDZjNGQ3ZDRjNzNhNDFlZTFkNDBlM2ExY2EzZGZhNyIsIkMiOiIwMjMyMTIzN2JlYjcyMWU3NGI1NzcwNWE5MjJjNjUxMGQwOTYyYzAzNzlhZDM0OTJhMDYwMDliZTAyNjA5ZjA3NTAiLCJyZXNlcnZlZCI6ZmFsc2UsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSJ9LHsiYW1vdW50IjoxLCJzZWNyZXQiOiJhNzdhM2NjODY4YWM4ZGU3YmNiOWMxMzJmZWI3YzEzMDY4Nzg3ODk5Yzk3YTk2NWE2ZThkZTFiMzliMmQ2NmQ3IiwiQyI6IjAzMTY0YTMxNWVhNjM0NGE5NWI2NzM1NzBkYzg0YmZlMTQ2NDhmMTQwM2EwMDJiZmJlMDhlNWFhMWE0NDQ0YWE0MCIsInJlc2VydmVkIjpmYWxzZSwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIn1dLCJtaW50IjoiaHR0cHM6Ly90ZXN0bnV0LmNhc2h1LnNwYWNlIn1dLCJ1bml0Ijoic2F0In0="; - let client = Client::new("https://cashu-rs.thesimplekid.space/").unwrap(); - - let wallet = Wallet::new(client, keys.keys); - - let blinded_messages = BlindedMessages::random(Amount::from_sat(64)).unwrap(); - - let mint_request = nut04::MintRequest { - outputs: blinded_messages.blinded_messages.clone(), - }; - - let res = mint.process_mint_request(mint_request).unwrap(); - - let proofs = wallet - .process_split_response(blinded_messages, res.promises) - .unwrap(); - for proof in &proofs { - mint.verify_proof(proof).unwrap(); - } - - let split = wallet.create_split(proofs.clone()).unwrap(); - - let split_request = split.split_payload; - - let split_response = mint.process_split_request(split_request).unwrap(); - let p = split_response.promises; - - let snd_proofs = wallet - .process_split_response(split.blinded_messages, p.unwrap()) - .unwrap(); - - let mut error = false; - for proof in &snd_proofs { - if let Err(err) = mint.verify_proof(proof) { - println!("{err}{:?}", serde_json::to_string(proof)); - error = true; - } - } - - if error { - panic!() - } + assert_eq!(token, token_str) } } -*/ diff --git a/misc/scripts/check-crates.sh b/misc/scripts/check-crates.sh index eb066cbe..e03b4d2a 100755 --- a/misc/scripts/check-crates.sh +++ b/misc/scripts/check-crates.sh @@ -26,9 +26,11 @@ buildargs=( "-p cdk" "-p cdk --no-default-features" "-p cdk --no-default-features --features wallet" + "-p cdk --no-default-features --features wallet --features nostr" "-p cdk --no-default-features --features mint" "-p cdk-redb" "-p cdk-redb --no-default-features --features wallet" + "-p cdk-redb --no-default-features --features wallet --features nostr" "-p cdk-redb --no-default-features --features mint" )