Browse Source

mint tests

thesimplekid 2 years ago
parent
commit
e93104a3f1
7 changed files with 104 additions and 8 deletions
  1. 1 0
      Cargo.toml
  2. 6 5
      src/cashu_mint.rs
  3. 0 1
      src/dhke.rs
  4. 1 0
      src/lib.rs
  5. 39 1
      src/types.rs
  6. 36 0
      src/utils.rs
  7. 21 1
      tests/integration_test.rs

+ 1 - 0
Cargo.toml

@@ -9,6 +9,7 @@ bitcoin_hashes = "0.12.0"
 hex = "0.4.3"
 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"] }
 serde = { version = "1.0.160", features = ["derive"]}
 thiserror = "1.0.40"

+ 6 - 5
src/cashu_mint.rs

@@ -5,9 +5,10 @@ use url::Url;
 use crate::{
     error::Error,
     types::{
-        BlindedMessage, CheckFeesRequest, CheckFeesResponse, CheckSpendableRequest,
-        CheckSpendableResponse, MeltRequest, MeltResposne, MintInfo, MintKeySets, MintKeys,
-        MintRequest, PostMintResponse, Proof, RequestMintResponse, SplitRequest, SplitResponse,
+        BlindedMessage, BlindedMessages, CheckFeesRequest, CheckFeesResponse,
+        CheckSpendableRequest, CheckSpendableResponse, MeltRequest, MeltResposne, MintInfo,
+        MintKeySets, MintKeys, MintRequest, PostMintResponse, Proof, RequestMintResponse,
+        SplitRequest, SplitResponse,
     },
 };
 
@@ -45,7 +46,7 @@ impl CashuMint {
     /// Mint Tokens [NUT-04]
     pub async fn mint(
         &self,
-        blinded_messages: Vec<BlindedMessage>,
+        blinded_messages: BlindedMessages,
         payment_hash: &str,
     ) -> Result<PostMintResponse, Error> {
         let mut url = self.url.join("mint")?;
@@ -53,7 +54,7 @@ impl CashuMint {
             .append_pair("payment_hash", payment_hash);
 
         let request = MintRequest {
-            outputs: blinded_messages,
+            outputs: blinded_messages.blinded_messages,
         };
 
         Ok(minreq::post(url)

+ 0 - 1
src/dhke.rs

@@ -1,7 +1,6 @@
 //! Diffie-Hellmann key exchange
 
 use bitcoin_hashes::sha256;
-// use bitcoin_hashes::Hash;
 use bitcoin_hashes::Hash;
 use secp256k1::rand::rngs::OsRng;
 use secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};

+ 1 - 0
src/lib.rs

@@ -2,3 +2,4 @@ pub mod cashu_mint;
 pub mod dhke;
 pub mod error;
 pub mod types;
+pub mod utils;

+ 39 - 1
src/types.rs

@@ -4,8 +4,12 @@ use std::collections::HashMap;
 
 use bitcoin::Amount;
 use lightning_invoice::Invoice;
+use rand::Rng;
+use secp256k1::{PublicKey, SecretKey};
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 
+use crate::{dhke::blind_message, error::Error, utils::split_amount};
+
 /// Blinded Message [NUT-00]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct BlindedMessage {
@@ -14,7 +18,41 @@ pub struct BlindedMessage {
     pub amount: Amount,
     /// encrypted secret message (B_)
     #[serde(rename = "B_")]
-    pub b: String,
+    pub b: PublicKey,
+}
+
+/// Blinded Messages [NUT-00]
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct BlindedMessages {
+    /// Blinded messages
+    pub blinded_messages: Vec<BlindedMessage>,
+    /// Secrets
+    pub secrets: Vec<Vec<u8>>,
+    /// Rs
+    pub rs: Vec<SecretKey>,
+    /// Amounts
+    pub amounts: Vec<Amount>,
+}
+
+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 blinded_message = BlindedMessage { amount, b: blinded };
+
+            blinded_messages.secrets.push(bytes.to_vec());
+            blinded_messages.blinded_messages.push(blinded_message);
+            blinded_messages.rs.push(r);
+            blinded_messages.amounts.push(amount);
+        }
+
+        Ok(blinded_messages)
+    }
 }
 
 /// Promise (BlindedSignature) [NIP-00]

+ 36 - 0
src/utils.rs

@@ -0,0 +1,36 @@
+use bitcoin::Amount;
+
+/// Split amount into cashu denominations (powers of 2)
+pub fn split_amount(amount: Amount) -> Vec<Amount> {
+    let mut chunks = Vec::new();
+    let value = amount.to_sat();
+    for i in 0..64 {
+        let mask = 1 << i;
+        if (value & mask) != 0 {
+            chunks.push(Amount::from_sat(2u64.pow(i as u32)));
+        }
+    }
+    chunks
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_split_amount() {
+        assert_eq!(split_amount(Amount::from_sat(1)), vec![Amount::from_sat(1)]);
+        assert_eq!(split_amount(Amount::from_sat(2)), vec![Amount::from_sat(2)]);
+        assert_eq!(
+            split_amount(Amount::from_sat(3)),
+            vec![Amount::from_sat(1), Amount::from_sat(2)]
+        );
+        let amounts: Vec<Amount> = vec![1, 2, 8].iter().map(|a| Amount::from_sat(*a)).collect();
+        assert_eq!(split_amount(Amount::from_sat(11)), amounts);
+        let amounts: Vec<Amount> = vec![1, 2, 4, 8, 16, 32, 64, 128]
+            .iter()
+            .map(|a| Amount::from_sat(*a))
+            .collect();
+        assert_eq!(split_amount(Amount::from_sat(255)), amounts);
+    }
+}

+ 21 - 1
tests/integration_test.rs

@@ -1,10 +1,12 @@
 use std::str::FromStr;
+use std::thread;
+use std::time::Duration;
 
 use bitcoin::Amount;
 use lightning_invoice::Invoice;
 use url::Url;
 
-use cashu_rs::cashu_mint::CashuMint;
+use cashu_rs::{cashu_mint::CashuMint, types::BlindedMessages};
 
 const MINTURL: &str = "https://legend.lnbits.com/cashu/api/v1/SKvHRus9dmjWHhstHrsazW/";
 
@@ -37,6 +39,24 @@ async fn test_request_mint() {
 }
 
 #[tokio::test]
+async fn test_mint() {
+    let url = Url::from_str(MINTURL).unwrap();
+    let mint = CashuMint::new(url);
+    let mint_req = mint.request_mint(Amount::from_sat(21)).await.unwrap();
+    println!("Mint Req: {:?}", mint_req.pr.to_string());
+
+    // Since before the mind happens the invoice in the int 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));
+
+    let blinded_messages = BlindedMessages::random(Amount::from_sat(21)).unwrap();
+    let mint_res = mint.mint(blinded_messages, &mint_req.hash).await.unwrap();
+
+    println!("Mint: {:?}", mint_res);
+}
+
+#[tokio::test]
 async fn test_check_fees() {
     let invoice = Invoice::from_str("lnbc10n1p3a6s0dsp5n55r506t2fv4r0mjcg30v569nk2u9s40ur4v3r3mgtscjvkvnrqqpp5lzfv8fmjzduelk74y9rsrxrayvhyzcdsh3zkdgv0g50napzalvqsdqhf9h8vmmfvdjn5gp58qengdqxq8p3aaymdcqpjrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glc7z70cgqqg4sqqqqqqqlgqqqqrucqjq9qyysgqrjky5axsldzhqsjwsc38xa37k6t04le3ws4t26nqej62vst5xkz56qw85r6c4a3tr79588e0ceuuahwgfnkqc6n6269unlwqtvwr5vqqy0ncdq").unwrap();