thesimplekid 1 年之前
父節點
當前提交
ee696ba4ab
共有 9 個文件被更改,包括 310 次插入89 次删除
  1. 4 1
      Cargo.toml
  2. 1 0
      src/cashu_mint.rs
  3. 7 1
      src/cashu_wallet.rs
  4. 159 58
      src/dhke.rs
  5. 0 3
      src/error.rs
  6. 46 0
      src/serde_utils.rs
  7. 28 17
      src/types.rs
  8. 9 0
      src/utils.rs
  9. 56 9
      tests/integration_test.rs

+ 4 - 1
Cargo.toml

@@ -12,13 +12,16 @@ description = "Cashu rust library"
 [dependencies]
 base64 = "0.21.0"
 bitcoin = { version = "0.30.0", features=["serde"] }
+bitcoin-private = "0.1.0"
 bitcoin_hashes = "0.12.0"
 hex = "0.4.3"
+k256 = { version = "0.13.1", features=["arithmetic"] }
 lightning-invoice = { version = "0.22.0", features=["serde"] }
 minreq = { version = "2.7.0", features = ["json-using-serde", "https"] }
 rand = "0.8.5"
-secp256k1 = { version = "0.27.0", features = ["rand-std", "bitcoin-hashes-std"] }
+getrandom = { version = "0.2", features = ["js"] }
 serde = { version = "1.0.160", features = ["derive"]}
+serde_bytes = "0.11.9"
 serde_json = "1.0.96"
 thiserror = "1.0.40"
 url = "2.3.1"

+ 1 - 0
src/cashu_mint.rs

@@ -13,6 +13,7 @@ use crate::{
     },
 };
 
+#[derive(Debug, Clone)]
 pub struct CashuMint {
     pub url: Url,
 }

+ 7 - 1
src/cashu_wallet.rs

@@ -122,6 +122,7 @@ impl CashuWallet {
             proofs,
             outputs,
         };
+        println!("splint JSON {:?}", serde_json::to_string(&split_payload));
 
         Ok(SplitPayload {
             keep_blinded_messages,
@@ -136,21 +137,23 @@ impl CashuWallet {
         let mut send_proofs = SendProofs::default();
 
         for proof in proofs {
-            amount_avaliable += proof.amount;
             if amount_avaliable > amount {
                 send_proofs.change_proofs.push(proof);
                 break;
             } else {
+                amount_avaliable += proof.amount;
                 send_proofs.send_proofs.push(proof);
             }
         }
 
         if amount_avaliable.lt(&amount) {
+            println!("Not enough funds");
             return Err(Error::InsufficantFunds);
         }
 
         // If amount avaliable is EQUAL to send amount no need to split
         if amount_avaliable.eq(&amount) {
+            println!("Equal Proofs: {:#?}", send_proofs);
             return Ok(send_proofs);
         }
 
@@ -179,6 +182,9 @@ impl CashuWallet {
             &self.keys,
         )?;
 
+        println!("Send Proofs: {:#?}", send_proofs);
+        println!("Keep Proofs: {:#?}", keep_proofs);
+
         Ok(SendProofs {
             change_proofs: keep_proofs,
             send_proofs,

+ 159 - 58
src/dhke.rs

@@ -1,120 +1,167 @@
 //! Diffie-Hellmann key exchange
 
-use std::str::FromStr;
+use std::ops::{Add, Mul, Neg};
 
 use bitcoin_hashes::sha256;
 use bitcoin_hashes::Hash;
-use secp256k1::rand::rngs::OsRng;
-use secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
+// use secp256k1::rand::rngs::OsRng;
+// use secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
+
+use k256::Scalar;
+use k256::{AffinePoint, ProjectivePoint, PublicKey, Secp256k1, SecretKey};
+
+use rand::rngs::OsRng;
 
 use crate::error::Error;
 use crate::types::MintKeys;
 use crate::types::Promise;
 use crate::types::Proof;
 
-/// Hash to Curve
-pub fn hash_to_curve(secret_message: &[u8]) -> Result<PublicKey, Error> {
-    let mut msg = secret_message.to_vec();
+fn hash_to_curve(message: &[u8]) -> PublicKey {
+    let mut msg_to_hash = message.to_vec();
+
     loop {
-        let hash = sha256::Hash::hash(&msg);
-        let mut pubkey_bytes = vec![0x02];
-        pubkey_bytes.extend_from_slice(&hash[..]);
-
-        match PublicKey::from_slice(&pubkey_bytes) {
-            Ok(pubkey) => return Ok(pubkey),
-            Err(_) => {
-                msg = hash.to_byte_array().to_vec();
-            }
+        let hash = sha256::Hash::hash(&msg_to_hash);
+        match PublicKey::from_sec1_bytes(
+            &[0x02u8]
+                .iter()
+                .chain(&hash.to_byte_array())
+                .cloned()
+                .collect::<Vec<u8>>(),
+        ) {
+            Ok(pubkey) => return pubkey,
+            Err(_) => msg_to_hash = hash.to_byte_array().to_vec(),
         }
     }
 }
 
-/// Blind Message
+/// Blind Message Alice Step one
 pub fn blind_message(
     secret: &[u8],
     blinding_factor: Option<SecretKey>,
 ) -> Result<(PublicKey, SecretKey), Error> {
-    let y = hash_to_curve(secret)?;
+    let y = hash_to_curve(secret);
 
-    let secp = Secp256k1::new();
     let r: SecretKey = match blinding_factor {
         Some(sec_key) => sec_key,
-        None => {
-            let (secret_key, _public_key) = secp.generate_keypair(&mut OsRng);
-            secret_key
-        }
+        None => SecretKey::random(&mut rand::thread_rng()),
     };
 
-    let b = y.combine(&r.public_key(&secp))?;
+    let b = ProjectivePoint::from(y) + ProjectivePoint::from(&r.public_key());
 
-    Ok((b, r))
+    Ok((PublicKey::try_from(b).unwrap(), r))
 }
 
-/// Unblind Message
+/// Unblind Message (Alice Step 3)
 pub fn unblind_message(
+    // C_
     blinded_key: PublicKey,
     r: SecretKey,
-    a: PublicKey,
+    // A
+    mint_pubkey: PublicKey,
 ) -> Result<PublicKey, Error> {
-    let secp = Secp256k1::new();
-    let a_neg = a.negate(&secp);
-    let blinded_key = blinded_key.combine(&a_neg).unwrap();
-    let unblinded_key =
-        blinded_key.mul_tweak(&secp, &Scalar::from_be_bytes(r.secret_bytes()).unwrap())?;
-    Ok(unblinded_key)
+    // C
+    // Unblinded message
+    let c = ProjectivePoint::from(blinded_key.as_affine())
+        - mint_pubkey
+            .as_affine()
+            .mul(Scalar::from(r.as_scalar_primitive()));
+
+    Ok(PublicKey::try_from(c).unwrap())
+}
+
+/// Sign Blinded Message (Step2 bob)
+// Really only needed for mint
+// Used here for testing
+fn _sign_message(a: SecretKey, blinded_message: PublicKey) -> Result<PublicKey, Error> {
+    Ok(PublicKey::try_from(
+        blinded_message
+            .as_affine()
+            .mul(Scalar::from(a.as_scalar_primitive())),
+    )
+    .unwrap())
+}
+
+/// Verify Message
+// Really only needed for mint
+// used for testing
+fn _verify_message(a: SecretKey, unblinded_message: PublicKey, msg: &str) -> Result<bool, Error> {
+    // Y
+    let y = hash_to_curve(msg.as_bytes());
+
+    Ok(unblinded_message
+        == PublicKey::try_from(*y.as_affine() * Scalar::from(a.as_scalar_primitive())).unwrap())
 }
 
 /// Construct Proof
 pub fn construct_proof(
     promises: Vec<Promise>,
     rs: Vec<SecretKey>,
-    secrets: Vec<Vec<u8>>,
+    secrets: Vec<String>,
     keys: &MintKeys,
 ) -> Result<Vec<Proof>, Error> {
     let mut proofs = vec![];
     for (i, promise) in promises.into_iter().enumerate() {
-        let blinded_c = PublicKey::from_str(&promise.c)?;
-        let a: PublicKey = PublicKey::from_str(keys.0.get(&promise.amount.to_sat()).unwrap())?;
+        let blinded_c = promise.c;
+        let a: PublicKey = PublicKey::from_sec1_bytes(
+            keys.0
+                .get(&promise.amount.to_sat())
+                .unwrap()
+                .to_owned()
+                .as_bytes(),
+        )
+        .unwrap();
+        // println!("Construct proof Pub {:?}", serde_json::to_string(&a));
+        todo!();
         let unblinded_signature = unblind_message(blinded_c, rs[i], a)?;
 
         let proof = Proof {
             id: Some(promise.id),
             amount: promise.amount,
-            secret: hex::encode(&secrets[i]),
-            c: unblinded_signature.to_string(),
+            secret: secrets[i].clone(),
+            c: unblinded_signature,
             script: None,
         };
 
         proofs.push(proof);
     }
 
+    println!("proofs: {:?}", proofs);
+
     Ok(proofs)
 }
+pub fn verify_proof(proof: Proof, keys: &MintKeys) -> Result<(), Error> {
+    Ok(())
+}
 
 #[cfg(test)]
 mod tests {
     use hex::decode;
-    use std::str::FromStr;
+
+    use k256::elliptic_curve::scalar::ScalarPrimitive;
 
     use super::*;
+    use crate::utils::generate_secret;
 
     #[test]
     fn test_hash_to_curve() {
         let secret = "0000000000000000000000000000000000000000000000000000000000000000";
         let sec_hex = decode(secret).unwrap();
 
-        let y = hash_to_curve(&sec_hex).unwrap();
-        let expected_y = PublicKey::from_str(
-            "0266687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925",
+        let y = hash_to_curve(&sec_hex);
+        let expected_y = PublicKey::from_sec1_bytes(
+            &hex::decode("0266687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925")
+                .unwrap(),
         )
         .unwrap();
         assert_eq!(y, expected_y);
 
         let secret = "0000000000000000000000000000000000000000000000000000000000000001";
         let sec_hex = decode(secret).unwrap();
-        let y = hash_to_curve(&sec_hex).unwrap();
-        let expected_y = PublicKey::from_str(
-            "02ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5",
+        let y = hash_to_curve(&sec_hex);
+        let expected_y = PublicKey::from_sec1_bytes(
+            &hex::decode("02ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5")
+                .unwrap(),
         )
         .unwrap();
         assert_eq!(y, expected_y);
@@ -123,42 +170,96 @@ mod tests {
     #[test]
     fn test_blind_message() {
         let message = "test_message";
-        let blinding_factor = "0000000000000000000000000000000000000000000000000000000000000001";
-        let sec = SecretKey::from_str(blinding_factor).unwrap();
+        let sec = SecretKey::new(ScalarPrimitive::ONE);
 
-        let (b, r) = blind_message(message.as_bytes(), Some(sec)).unwrap();
+        let (b, r) = blind_message(message.as_bytes(), Some(sec.clone())).unwrap();
 
         assert_eq!(
-            b.to_string(),
-            "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2".to_string()
+            b,
+            PublicKey::from_sec1_bytes(
+                &hex::decode("02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2")
+                    .unwrap()
+            )
+            .unwrap()
         );
 
         assert_eq!(r, sec);
     }
 
     #[test]
+    fn test_sign_message() {
+        let message = "test_message";
+        let sec = SecretKey::new(ScalarPrimitive::ONE);
+
+        let (blinded_message, _r) = blind_message(message.as_bytes(), Some(sec)).unwrap();
+
+        // A
+        let bob_sec = SecretKey::new(ScalarPrimitive::ONE);
+
+        // C_
+        let signed = _sign_message(bob_sec, blinded_message).unwrap();
+
+        assert_eq!(
+            signed,
+            PublicKey::from_sec1_bytes(
+                &hex::decode("02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2")
+                    .unwrap()
+            )
+            .unwrap()
+        );
+    }
+
+    #[test]
     fn test_unblind_message() {
-        let blinded_key = PublicKey::from_str(
-            "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
+        let blinded_key = PublicKey::from_sec1_bytes(
+            &hex::decode("02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2")
+                .unwrap(),
         )
         .unwrap();
 
-        let r =
-            SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
-                .unwrap();
-        let a = PublicKey::from_str(
-            "020000000000000000000000000000000000000000000000000000000000000001",
+        let r = SecretKey::new(ScalarPrimitive::ONE);
+        let a = PublicKey::from_sec1_bytes(
+            &hex::decode("020000000000000000000000000000000000000000000000000000000000000001")
+                .unwrap(),
         )
         .unwrap();
 
         let unblinded = unblind_message(blinded_key, r, a).unwrap();
 
         assert_eq!(
-            PublicKey::from_str(
-                "03c724d7e6a5443b39ac8acf11f40420adc4f99a02e7cc1b57703d9391f6d129cd"
+            PublicKey::from_sec1_bytes(
+                &hex::decode("03c724d7e6a5443b39ac8acf11f40420adc4f99a02e7cc1b57703d9391f6d129cd")
+                    .unwrap()
             )
             .unwrap(),
             unblinded
         );
     }
+
+    #[test]
+    fn test_blinded_dhke() {
+        // a
+        let bob_sec = SecretKey::random(&mut rand::thread_rng());
+
+        // A
+        let bob_pub = bob_sec.public_key();
+
+        // let alice_sec = SecretKey::random(&mut rand::thread_rng());
+
+        let x = generate_secret();
+
+        // Y
+        let y = hash_to_curve(x.as_bytes());
+
+        // B_
+        let blinded = blind_message(&y.to_sec1_bytes(), None).unwrap();
+
+        // C_
+        let signed = _sign_message(bob_sec.clone(), blinded.0).unwrap();
+
+        // C
+        let c = unblind_message(signed, blinded.1, bob_pub).unwrap();
+
+        assert!(_verify_message(bob_sec, c, &x).unwrap());
+    }
 }

+ 0 - 3
src/error.rs

@@ -8,9 +8,6 @@ pub enum Error {
     /// Parse Url Error
     #[error("minreq error: {0}")]
     UrlParseError(#[from] url::ParseError),
-    /// Secp245k1
-    #[error("secp256k1 error: {0}")]
-    Secpk256k1Error(#[from] secp256k1::Error),
     /// Unsupported Token
     #[error("Unsupported Token")]
     UnsupportedToken,

+ 46 - 0
src/serde_utils.rs

@@ -19,3 +19,49 @@ pub mod serde_url {
         Url::parse(&url_string).map_err(serde::de::Error::custom)
     }
 }
+
+pub mod bytes_base64 {
+    use base64::{engine::general_purpose, Engine as _};
+    use serde::Deserialize;
+
+    pub fn serialize<S>(my_bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let encoded = general_purpose::STANDARD.encode(my_bytes);
+        serializer.serialize_str(&encoded)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let encoded = String::deserialize(deserializer)?;
+        let decoded = general_purpose::STANDARD
+            .decode(encoded)
+            .map_err(serde::de::Error::custom)?;
+        Ok(decoded)
+    }
+}
+
+pub mod serde_public_key {
+    use k256::PublicKey;
+    use serde::Deserialize;
+
+    pub fn serialize<S>(pubkey: &PublicKey, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let encoded = hex::encode(pubkey.to_sec1_bytes());
+        serializer.serialize_str(&encoded)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let encoded = String::deserialize(deserializer)?;
+        let decoded = hex::decode(encoded).map_err(serde::de::Error::custom)?;
+        PublicKey::from_sec1_bytes(&decoded).map_err(serde::de::Error::custom)
+    }
+}

+ 28 - 17
src/types.rs

@@ -4,13 +4,15 @@ use std::{collections::HashMap, str::FromStr};
 
 use base64::{engine::general_purpose, Engine as _};
 use bitcoin::Amount;
+use k256::{PublicKey, SecretKey};
 use lightning_invoice::Invoice;
-use rand::Rng;
-use secp256k1::{PublicKey, SecretKey};
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 use url::Url;
 
-use crate::{dhke::blind_message, error::Error, serde_utils::serde_url, utils::split_amount};
+use crate::utils::generate_secret;
+use crate::{
+    dhke::blind_message, error::Error, serde_utils, serde_utils::serde_url, utils::split_amount,
+};
 
 /// Blinded Message [NUT-00]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -20,6 +22,7 @@ pub struct BlindedMessage {
     pub amount: Amount,
     /// encrypted secret message (B_)
     #[serde(rename = "B_")]
+    #[serde(with = "serde_utils::serde_public_key")]
     pub b: PublicKey,
 }
 
@@ -29,7 +32,7 @@ pub struct BlindedMessages {
     /// Blinded messages
     pub blinded_messages: Vec<BlindedMessage>,
     /// Secrets
-    pub secrets: Vec<Vec<u8>>,
+    pub secrets: Vec<String>,
     /// Rs
     pub rs: Vec<SecretKey>,
     /// Amounts
@@ -40,14 +43,13 @@ impl BlindedMessages {
     pub fn random(amount: Amount) -> Result<Self, Error> {
         let mut blinded_messages = BlindedMessages::default();
 
-        let mut rng = rand::thread_rng();
         for amount in split_amount(amount) {
-            let bytes: [u8; 32] = rng.gen();
-            let (blinded, r) = blind_message(&bytes, None)?;
+            let secret = generate_secret();
+            let (blinded, r) = blind_message(secret.as_bytes(), None)?;
 
             let blinded_message = BlindedMessage { amount, b: blinded };
 
-            blinded_messages.secrets.push(bytes.to_vec());
+            blinded_messages.secrets.push(secret);
             blinded_messages.blinded_messages.push(blinded_message);
             blinded_messages.rs.push(r);
             blinded_messages.amounts.push(amount);
@@ -56,20 +58,23 @@ impl BlindedMessages {
         Ok(blinded_messages)
     }
 
+    /*
+
     pub fn blank() -> Result<Self, Error> {
         let mut blinded_messages = BlindedMessages::default();
 
         let mut rng = rand::thread_rng();
         for _i in 0..4 {
             let bytes: [u8; 32] = rng.gen();
-            let (blinded, r) = blind_message(&bytes, None)?;
+            let secret_base64 = general_purpose::STANDARD.encode(bytes);
+            let (blinded, r) = blind_message(secret_base64.as_bytes(), None)?;
 
             let blinded_message = BlindedMessage {
                 amount: Amount::ZERO,
                 b: blinded,
             };
 
-            blinded_messages.secrets.push(bytes.to_vec());
+            blinded_messages.secrets.push(secret_base64);
             blinded_messages.blinded_messages.push(blinded_message);
             blinded_messages.rs.push(r);
             blinded_messages.amounts.push(Amount::ZERO);
@@ -77,6 +82,7 @@ impl BlindedMessages {
 
         Ok(blinded_messages)
     }
+    */
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -86,7 +92,7 @@ pub struct SplitPayload {
     pub split_payload: SplitRequest,
 }
 
-/// Promise (BlindedSignature) [NIP-00]
+/// Promise (BlindedSignature) [NUT-00]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct Promise {
     pub id: String,
@@ -94,7 +100,8 @@ pub struct Promise {
     pub amount: Amount,
     /// blinded signature (C_) on the secret message `B_` of [BlindedMessage]
     #[serde(rename = "C_")]
-    pub c: String,
+    #[serde(with = "serde_utils::serde_public_key")]
+    pub c: PublicKey,
 }
 
 /// Proofs [NUT-00]
@@ -104,21 +111,24 @@ pub struct Proof {
     #[serde(with = "bitcoin::amount::serde::as_sat")]
     pub amount: Amount,
     /// Secret message
+    // #[serde(with = "crate::serde_utils::bytes_base64")]
     pub secret: String,
     /// Unblinded signature
     #[serde(rename = "C")]
-    pub c: String,
+    #[serde(with = "serde_utils::serde_public_key")]
+    pub c: PublicKey,
     /// `Keyset id`
     pub id: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
     /// P2SHScript that specifies the spending condition for this Proof
     pub script: Option<String>,
 }
 
-/// Mint Keys [NIP-01]
+/// Mint Keys [NUT-01]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MintKeys(pub HashMap<u64, String>);
 
-/// Mint Keysets [NIP-02]
+/// Mint Keysets [UT-02]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MintKeySets {
     /// set of public keys that the mint generates
@@ -134,7 +144,7 @@ pub struct RequestMintResponse {
     pub hash: String,
 }
 
-/// Post Mint Request [NIP-04]
+/// Post Mint Request [NUT-04]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MintRequest {
     pub outputs: Vec<BlindedMessage>,
@@ -266,7 +276,8 @@ pub struct MintInfo {
     /// name of the mint and should be recognizable
     pub name: String,
     /// hex pubkey of the mint
-    pub pubkey: String,
+    #[serde(with = "serde_utils::serde_public_key")]
+    pub pubkey: PublicKey,
     /// implementation name and the version running
     pub version: MintVersion,
     /// short description of the mint

+ 9 - 0
src/utils.rs

@@ -1,4 +1,6 @@
+use base64::{engine::general_purpose, Engine as _};
 use bitcoin::Amount;
+use rand::prelude::*;
 
 /// Split amount into cashu denominations (powers of 2)
 pub fn split_amount(amount: Amount) -> Vec<Amount> {
@@ -13,6 +15,13 @@ pub fn split_amount(amount: Amount) -> Vec<Amount> {
     chunks
 }
 
+pub fn generate_secret() -> String {
+    let mut rng = rand::thread_rng();
+    let mut secret = [0u8; 32];
+    rng.fill_bytes(&mut secret);
+    general_purpose::STANDARD.encode(secret)
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;

+ 56 - 9
tests/integration_test.rs

@@ -6,10 +6,11 @@ use bitcoin::Amount;
 use lightning_invoice::Invoice;
 use url::Url;
 
-use cashu_rs::{cashu_mint::CashuMint, cashu_wallet::CashuWallet, types::BlindedMessages};
+use cashu_rs::{cashu_mint::CashuMint, cashu_wallet::CashuWallet, types::{BlindedMessages, TokenData}};
 
 const MINTURL: &str = "https://legend.lnbits.com/cashu/api/v1/SKvHRus9dmjWHhstHrsazW/";
 
+#[ignore]
 #[tokio::test]
 async fn test_get_mint_keys() {
     let url = Url::from_str(MINTURL).unwrap();
@@ -19,6 +20,7 @@ async fn test_get_mint_keys() {
     assert!(mint_keys.0.capacity() > 1);
 }
 
+#[ignore]
 #[tokio::test]
 async fn test_get_mint_keysets() {
     let url = Url::from_str(MINTURL).unwrap();
@@ -28,6 +30,7 @@ async fn test_get_mint_keysets() {
     assert!(!mint_keysets.keysets.is_empty())
 }
 
+#[ignore]
 #[tokio::test]
 async fn test_request_mint() {
     let url = Url::from_str(MINTURL).unwrap();
@@ -49,7 +52,7 @@ async fn test_mint() {
     // Since before the mint happens the invoice in the mint req has to be payed this wait is here
     // probally some way to simulate this in a better way
     // but for now pay it quick
-    thread::sleep(Duration::from_secs(10));
+    thread::sleep(Duration::from_secs(30));
 
     let blinded_messages = BlindedMessages::random(Amount::from_sat(21)).unwrap();
     let mint_res = mint.mint(blinded_messages, &mint_req.hash).await.unwrap();
@@ -57,6 +60,7 @@ async fn test_mint() {
     println!("Mint: {:?}", mint_res);
 }
 
+#[ignore]
 #[tokio::test]
 async fn test_check_fees() {
     let invoice = Invoice::from_str("lnbc10n1p3a6s0dsp5n55r506t2fv4r0mjcg30v569nk2u9s40ur4v3r3mgtscjvkvnrqqpp5lzfv8fmjzduelk74y9rsrxrayvhyzcdsh3zkdgv0g50napzalvqsdqhf9h8vmmfvdjn5gp58qengdqxq8p3aaymdcqpjrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glc7z70cgqqg4sqqqqqqqlgqqqqrucqjq9qyysgqrjky5axsldzhqsjwsc38xa37k6t04le3ws4t26nqej62vst5xkz56qw85r6c4a3tr79588e0ceuuahwgfnkqc6n6269unlwqtvwr5vqqy0ncdq").unwrap();
@@ -64,8 +68,8 @@ async fn test_check_fees() {
     let url = Url::from_str(MINTURL).unwrap();
     let mint = CashuMint::new(url);
 
-    let fee = mint.check_fees(invoice).await.unwrap();
-    println!("{fee:?}");
+    let _fee = mint.check_fees(invoice).await.unwrap();
+    // println!("{fee:?}");
 }
 
 #[ignore]
@@ -77,14 +81,56 @@ async fn test_receive() {
 
     let wallet = CashuWallet::new(mint, mint_keys);
     // FIXME: Have to manully paste an unspent token
-    let token = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6MiwiQyI6IjAzNmY1NTU0ZDMyZDg3MGFjMzZjMDIwOGNiMDlkZmJmZjNhN2RkZTUyNzMwOTNjYzk3ZjE2NDBkNjYyZTgyMmMyMCIsInNlY3JldCI6ImtuRlhvelpjUG5YK1l4dytIcmV3VVlXRHU2ZFVFbkY0KzRUTkRIN010V289In1dLCJtaW50IjoiaHR0cHM6Ly9sZWdlbmQubG5iaXRzLmNvbS9jYXNodS9hcGkvdjEvU0t2SFJ1czlkbWpXSGhzdEhyc2F6VyJ9XX0=";
+    let token = 
+    "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6MiwiQyI6IjAyOTMwNTJhNWEwN2FjMTkxMDgyODQyZTExMDVkOTQ2MzliNWI5NmE3MTU3NTQzZTllMjdkOTg3MWU5YjE2NDJkNCIsInNlY3JldCI6IlQxZ0lYUWlpZnBNY21OMU9ENnV4Nk1rMS93bXIxU3VHU2tvVXIyTkpqZE09In1dLCJtaW50IjoiaHR0cHM6Ly9sZWdlbmQubG5iaXRzLmNvbS9jYXNodS9hcGkvdjEvU0t2SFJ1czlkbWpXSGhzdEhyc2F6VyJ9XX0=";
 
     let prom = wallet.receive(token).await.unwrap();
-    println!("{:?}", prom);
+    // println!("{:?}", prom);
+}
+
+#[ignore]
+#[tokio::test]
+async fn test_check_spendable() {
+    let url = Url::from_str(MINTURL).unwrap();
+    let mint = CashuMint::new(url);
+    let mint_keys = mint.get_keys().await.unwrap();
+
+    let wallet = CashuWallet::new(mint, mint_keys);
+    // FIXME: Have to manully paste an unspent token
+    let token = 
+        "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6MiwiQyI6IjAyNGQ0ZDUxNWIxYzk2MWZkYzYxY2M5MDFmNzBkOGUwZDA0ZWIwYTI2MzBhNWYxYTdmM2I5ZmRhODdmMGJkNjNmNyIsInNlY3JldCI6IkVUc2pXSGJheXYyTUJQeXo1b0toay85dVdoaldIeXJkODdBQy9XY3VjbkE9In1dLCJtaW50IjoiaHR0cHM6Ly9sZWdlbmQubG5iaXRzLmNvbS9jYXNodS9hcGkvdjEvU0t2SFJ1czlkbWpXSGhzdEhyc2F6VyJ9XX0=";
+
+    let token_data = TokenData::from_str(token).unwrap();
+    let spendable = wallet.check_proofs_spent(token_data.token[0].clone().proofs).await.unwrap();
+    // println!("Spendable: {:?}", spendable);
+    
 }
 
 // #[ignore]
 #[tokio::test]
+async fn test_split() {
+    let url = Url::from_str("http://localhost:3338").unwrap();
+    let mint = CashuMint::new(url);
+    let mint_keys = mint.get_keys().await.unwrap();
+
+    let wallet = CashuWallet::new(mint.clone(), mint_keys);
+    // FIXME: Have to manully paste an unspent token
+    let token = 
+    "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6MiwiQyI6IjAyNDVjYjBhYzhlMWNmNGViMjk2ZjAyMTFiMDdjYTBjNTczOWM1MTMwMDEzMzM3MjczOTE1ZTVlMDY2NjZlOTBiZCIsInNlY3JldCI6ImRWNThLbU5VOWE0UU45c0QyVDd5bGkvam9qcWpwb3o0VVhkSGR6dkdRZ289In1dLCJtaW50IjoiaHR0cHM6Ly9sZWdlbmQubG5iaXRzLmNvbS9jYXNodS9hcGkvdjEvU0t2SFJ1czlkbWpXSGhzdEhyc2F6VyJ9XX0=";
+    let proofs = wallet.receive(token).await.unwrap();
+
+    let split = wallet.create_split(Amount::ONE_SAT, Amount::ONE_SAT, proofs).await.unwrap();
+ 
+    println!("Split: {:#?}", split);
+    println!("splint JSON {:?}", serde_json::to_string(&split.split_payload));
+
+    let split = mint.split(split.split_payload).await;
+    println!("Split res: {:#?}", split);
+}
+
+
+#[ignore]
+#[tokio::test]
 async fn test_send() {
     let url = Url::from_str(MINTURL).unwrap();
     let mint = CashuMint::new(url);
@@ -92,12 +138,13 @@ async fn test_send() {
 
     let wallet = CashuWallet::new(mint, mint_keys);
     // FIXME: Have to manully paste an unspent token
-    let token = "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6MSwiQyI6IjAyMjRhMjU5NGY5NWMyMmRiZTA2YjZlN2YzMDNkYTdiZWYwNmM1YzI5YTBjMDU3ZWYyNmNhOWU3ZDVlYzc3MTYzZiIsInNlY3JldCI6IncyL1FpZjZFdlBRYWRtUlYxZzQyTWMrZWVVZ1V3TVZtSC9ndlVlaHhZTXM9In0seyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6NCwiQyI6IjAyMWEwYTIwYTZmOGEwY2JmMWY2Njc5OTIzNWE5N2U4ZTgxNjkxZWExMTFkMWVjYWJiOWZlZjE5OWRhMzYxNmU0YiIsInNlY3JldCI6InFYazRGbjZKdFBaUnVIRWlFMVVBUDB4MCtEcjd4Y21yNWRwTUVRRldDZ2s9In1dLCJtaW50IjoiaHR0cHM6Ly9sZWdlbmQubG5iaXRzLmNvbS9jYXNodS9hcGkvdjEvU0t2SFJ1czlkbWpXSGhzdEhyc2F6VyJ9XX0=";
-
+    let token = 
+    "cashuAeyJ0b2tlbiI6W3sicHJvb2ZzIjpbeyJpZCI6Im9DV2NkWXJyeVRrUiIsImFtb3VudCI6MiwiQyI6IjAzMGI4NWFhYjI5MDY2MGRlNDk4NTEzODZmYTJhZWY2MTk3YzM2MzRkZDE4OGMzMjM2ZDI2YTFhNDdmODZlNzQxNCIsInNlY3JldCI6IjNET0c3eHM2T2RRYno1Nmk1c0lRQjhndHUzbjRMdjRGSU5TeEtLUkJ6UzA9In1dLCJtaW50IjoiaHR0cHM6Ly9sZWdlbmQubG5iaXRzLmNvbS9jYXNodS9hcGkvdjEvU0t2SFJ1czlkbWpXSGhzdEhyc2F6VyJ9XX0=";
     let prom = wallet.receive(token).await.unwrap();
-    let send = wallet.send(Amount::from_sat(1), prom).await.unwrap();
+    let send = wallet.send(Amount::from_sat(2), prom).await.unwrap();
 
     println!("{:?}", send);
+    panic!()
 }
 
 #[ignore]