thesimplekid преди 9 месеца
родител
ревизия
c64c741e14

+ 2 - 0
.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:

+ 1 - 1
.helix/languages.toml

@@ -1,2 +1,2 @@
 [language-server.rust-analyzer.config]
-cargo = { features = ["wallet", "mint"] }
+cargo = { features = ["wallet", "mint", "nostr"] }

+ 1 - 0
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

+ 47 - 0
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<Option<u32>, 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(())
+    }
 }

+ 5 - 0
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 = [

+ 15 - 1
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<Option<u32>, Self::Err>;
+
+    #[cfg(feature = "nostr")]
+    async fn get_nostr_last_checked(
+        &self,
+        verifying_key: &PublicKey,
+    ) -> Result<Option<u32>, Self::Err>;
+    #[cfg(feature = "nostr")]
+    async fn add_nostr_last_checked(
+        &self,
+        verifying_key: PublicKey,
+        last_checked: u32,
+    ) -> Result<(), Self::Err>;
 }
 
 #[cfg(feature = "mint")]

+ 34 - 0
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<RwLock<HashMap<UncheckedUrl, Option<MintInfo>>>>,
@@ -22,6 +25,8 @@ pub struct WalletMemoryDatabase {
     proofs: Arc<RwLock<HashMap<UncheckedUrl, HashSet<Proof>>>>,
     pending_proofs: Arc<RwLock<HashMap<UncheckedUrl, HashSet<Proof>>>>,
     keyset_counter: Arc<RwLock<HashMap<Id, u32>>>,
+    #[cfg(feature = "nostr")]
+    nostr_last_checked: Arc<RwLock<HashMap<PublicKey, u32>>>,
 }
 
 impl WalletMemoryDatabase {
@@ -30,6 +35,7 @@ impl WalletMemoryDatabase {
         melt_quotes: Vec<MeltQuote>,
         mint_keys: Vec<Keys>,
         keyset_counter: HashMap<Id, u32>,
+        #[cfg(feature = "nostr")] nostr_last_checked: HashMap<PublicKey, u32>,
     ) -> 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<Option<u32>, Error> {
         Ok(self.keyset_counter.read().await.get(id).cloned())
     }
+
+    #[cfg(feature = "nostr")]
+    async fn get_nostr_last_checked(
+        &self,
+        verifying_key: &PublicKey,
+    ) -> Result<Option<u32>, 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(())
+    }
 }

+ 3 - 0
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 },
 }

+ 30 - 1
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<secp256k1::PublicKey> for PublicKey {
     }
 }
 
+#[cfg(feature = "nostr")]
+impl TryFrom<PublicKey> for nostr_sdk::PublicKey {
+    type Error = Error;
+    fn try_from(pubkey: PublicKey) -> Result<Self, Self::Error> {
+        Ok(nostr_sdk::PublicKey::from_slice(&pubkey.to_bytes())?)
+    }
+}
+
+#[cfg(feature = "nostr")]
+impl TryFrom<nostr_sdk::PublicKey> for PublicKey {
+    type Error = Error;
+    fn try_from(pubkey: nostr_sdk::PublicKey) -> Result<Self, Self::Error> {
+        (&pubkey).try_into()
+    }
+}
+
+#[cfg(feature = "nostr")]
+impl TryFrom<&nostr_sdk::PublicKey> for PublicKey {
+    type Error = Error;
+    fn try_from(pubkey: &nostr_sdk::PublicKey) -> Result<Self, Self::Error> {
+        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 {

+ 16 - 0
crates/cdk/src/nuts/nut01/secret_key.rs

@@ -32,6 +32,22 @@ impl From<secp256k1::SecretKey> for SecretKey {
     }
 }
 
+#[cfg(feature = "nostr")]
+impl TryFrom<SecretKey> for nostr_sdk::SecretKey {
+    type Error = Error;
+    fn try_from(seckey: SecretKey) -> Result<Self, Self::Error> {
+        (&seckey).try_into()
+    }
+}
+
+#[cfg(feature = "nostr")]
+impl TryFrom<&SecretKey> for nostr_sdk::SecretKey {
+    type Error = Error;
+    fn try_from(seckey: &SecretKey) -> Result<Self, Self::Error> {
+        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())

+ 158 - 63
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<dyn WalletDatabase<Err = cdk_database::Error> + 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<String>) -> Result<(), Error> {
+        self.nostr_client.add_relays(relays).await?;
+        Ok(())
+    }
+
     /// Total Balance of wallet
     #[instrument(skip(self))]
     pub async fn total_balance(&self) -> Result<Amount, Error> {
@@ -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<Amount>,
@@ -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<Vec<SecretKey>>,
         preimages: Option<Vec<String>>,
@@ -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<Amount, Error> {
+        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<HashSet<String>> = 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)
     }
 }
-*/

+ 2 - 0
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"
 )