From ee696ba4abee75009495e033bc310b34a6b46a0f Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Tue, 25 Apr 2023 13:35:44 -0400 Subject: [PATCH] use k256 --- Cargo.toml | 5 +- src/cashu_mint.rs | 1 + src/cashu_wallet.rs | 8 +- src/dhke.rs | 217 ++++++++++++++++++++++++++++---------- src/error.rs | 3 - src/serde_utils.rs | 46 ++++++++ src/types.rs | 45 +++++--- src/utils.rs | 9 ++ tests/integration_test.rs | 65 ++++++++++-- 9 files changed, 310 insertions(+), 89 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2ed4396d..e74ac136 100644 --- a/Cargo.toml +++ b/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" diff --git a/src/cashu_mint.rs b/src/cashu_mint.rs index f26be6ab..1b9bc6ab 100644 --- a/src/cashu_mint.rs +++ b/src/cashu_mint.rs @@ -13,6 +13,7 @@ use crate::{ }, }; +#[derive(Debug, Clone)] pub struct CashuMint { pub url: Url, } diff --git a/src/cashu_wallet.rs b/src/cashu_wallet.rs index d0e137ce..0bf9e1d2 100644 --- a/src/cashu_wallet.rs +++ b/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, diff --git a/src/dhke.rs b/src/dhke.rs index 56ab0246..59fc89b1 100644 --- a/src/dhke.rs +++ b/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 { - let mut msg = secret_message.to_vec(); - loop { - let hash = sha256::Hash::hash(&msg); - let mut pubkey_bytes = vec![0x02]; - pubkey_bytes.extend_from_slice(&hash[..]); +fn hash_to_curve(message: &[u8]) -> PublicKey { + let mut msg_to_hash = message.to_vec(); - match PublicKey::from_slice(&pubkey_bytes) { - Ok(pubkey) => return Ok(pubkey), - Err(_) => { - msg = hash.to_byte_array().to_vec(); - } + loop { + let hash = sha256::Hash::hash(&msg_to_hash); + match PublicKey::from_sec1_bytes( + &[0x02u8] + .iter() + .chain(&hash.to_byte_array()) + .cloned() + .collect::>(), + ) { + 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, ) -> 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 { - 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 { + 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 { + // 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, rs: Vec, - secrets: Vec>, + secrets: Vec, keys: &MintKeys, ) -> Result, 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()); + } } diff --git a/src/error.rs b/src/error.rs index 2d6312d3..4b15b790 100644 --- a/src/error.rs +++ b/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, diff --git a/src/serde_utils.rs b/src/serde_utils.rs index ec3a62bb..23c1b219 100644 --- a/src/serde_utils.rs +++ b/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(my_bytes: &Vec, serializer: S) -> Result + where + S: serde::Serializer, + { + let encoded = general_purpose::STANDARD.encode(my_bytes); + serializer.serialize_str(&encoded) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, 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(pubkey: &PublicKey, serializer: S) -> Result + where + S: serde::Serializer, + { + let encoded = hex::encode(pubkey.to_sec1_bytes()); + serializer.serialize_str(&encoded) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + 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) + } +} diff --git a/src/types.rs b/src/types.rs index 0a087a41..977cee83 100644 --- a/src/types.rs +++ b/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, /// Secrets - pub secrets: Vec>, + pub secrets: Vec, /// Rs pub rs: Vec, /// Amounts @@ -40,14 +43,13 @@ impl BlindedMessages { pub fn random(amount: Amount) -> Result { 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 { 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, + #[serde(skip_serializing_if = "Option::is_none")] /// P2SHScript that specifies the spending condition for this Proof pub script: Option, } -/// Mint Keys [NIP-01] +/// Mint Keys [NUT-01] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct MintKeys(pub HashMap); -/// 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, @@ -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 diff --git a/src/utils.rs b/src/utils.rs index 4527bd38..a092cfae 100644 --- a/src/utils.rs +++ b/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 { @@ -13,6 +15,13 @@ pub fn split_amount(amount: Amount) -> Vec { 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::*; diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 2f847989..393c6d91 100644 --- a/tests/integration_test.rs +++ b/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]