From d3c7420f91dec714875525fd3d1941c9e0b37898 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sat, 24 Jun 2023 16:59:50 -0400 Subject: [PATCH] fix split --- src/dhke.rs | 5 +- src/lib.rs | 2 +- src/mint.rs | 9 +- src/nuts/nut01.rs | 6 +- src/{cashu_wallet.rs => wallet.rs} | 150 ++++++++++++++++++++++++++++- 5 files changed, 161 insertions(+), 11 deletions(-) rename src/{cashu_wallet.rs => wallet.rs} (65%) diff --git a/src/dhke.rs b/src/dhke.rs index 386a752d..81969f9e 100644 --- a/src/dhke.rs +++ b/src/dhke.rs @@ -78,7 +78,7 @@ pub fn construct_proofs( for (i, promise) in promises.into_iter().enumerate() { let blinded_c = promise.c; let a: PublicKey = keys - .amount_key(&promise.amount.to_sat()) + .amount_key(promise.amount) .ok_or(Error::CustomError("Could not get proofs".to_string()))? .to_owned(); @@ -107,8 +107,7 @@ pub fn sign_message( blinded_message .as_affine() .mul(Scalar::from(a.as_scalar_primitive())), - ) - .unwrap()) + )?) } /// Verify Message diff --git a/src/lib.rs b/src/lib.rs index 0cf37ed6..5f191192 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ pub mod amount; -pub mod cashu_wallet; pub mod client; pub mod dhke; pub mod error; @@ -8,6 +7,7 @@ pub mod nuts; pub mod serde_utils; pub mod types; pub mod utils; +pub mod wallet; pub use amount::Amount; pub use bitcoin::hashes::sha256::Hash as Sha256; diff --git a/src/mint.rs b/src/mint.rs index bf089e17..3035d3b1 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -98,6 +98,8 @@ impl Mint { amount: Amount, outputs: &[BlindedMessage], ) -> Result { + let mut outputs = outputs.to_vec(); + outputs.reverse(); let mut target_total = Amount::ZERO; let mut change_total = Amount::ZERO; let mut target = Vec::with_capacity(outputs.len()); @@ -107,7 +109,7 @@ impl Mint { // in the outputs (blind messages). As we loop, take from those sets, // target amount first. for output in outputs { - let signed = self.blind_sign(output)?; + let signed = self.blind_sign(&output)?; // Accumulate outputs into the target (send) list if target_total + signed.amount <= amount { @@ -119,6 +121,9 @@ impl Mint { } } + println!("change: {:?}", serde_json::to_string(&change)); + println!("send: {:?}", serde_json::to_string(&target)); + Ok(SplitResponse { fst: change, snd: target, @@ -168,7 +173,7 @@ impl Mint { Ok(split_response) } - fn verify_proof(&self, proof: &Proof) -> Result { + pub fn verify_proof(&self, proof: &Proof) -> Result { if self.spent_secrets.contains(&proof.secret) { return Err(Error::TokenSpent); } diff --git a/src/nuts/nut01.rs b/src/nuts/nut01.rs index cb9f908a..1308f8cc 100644 --- a/src/nuts/nut01.rs +++ b/src/nuts/nut01.rs @@ -6,6 +6,8 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; +use crate::Amount; + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] pub struct PublicKey(#[serde(with = "crate::serde_utils::serde_public_key")] k256::PublicKey); @@ -57,8 +59,8 @@ impl Keys { self.0.clone() } - pub fn amount_key(&self, amount: &u64) -> Option { - self.0.get(amount).cloned() + pub fn amount_key(&self, amount: Amount) -> Option { + self.0.get(&amount.to_sat()).cloned() } pub fn as_hashmap(&self) -> HashMap { diff --git a/src/cashu_wallet.rs b/src/wallet.rs similarity index 65% rename from src/cashu_wallet.rs rename to src/wallet.rs index 519d0494..27241f21 100644 --- a/src/cashu_wallet.rs +++ b/src/wallet.rs @@ -1,7 +1,8 @@ //! Cashu Wallet use std::str::FromStr; -use crate::nuts::nut00::{mint, BlindedMessages, Proofs, Token}; +use crate::dhke::unblind_message; +use crate::nuts::nut00::{mint, BlindedMessages, BlindedSignature, Proof, Proofs, Token}; use crate::nuts::nut01::Keys; use crate::nuts::nut03::RequestMintResponse; use crate::nuts::nut06::{SplitPayload, SplitRequest}; @@ -12,13 +13,13 @@ use crate::{client::Client, dhke::construct_proofs, error::Error}; use crate::amount::Amount; #[derive(Clone, Debug)] -pub struct CashuWallet { +pub struct Wallet { pub client: Client, pub mint_keys: Keys, pub balance: Amount, } -impl CashuWallet { +impl Wallet { pub fn new(client: Client, mint_keys: Keys) -> Self { Self { client, @@ -151,6 +152,15 @@ impl CashuWallet { outputs, }; + println!( + "Keep blinded: {:?}", + serde_json::to_string(&keep_blinded_messages) + ); + println!( + "Send blinded: {:?}", + serde_json::to_string(&send_blinded_messages) + ); + Ok(SplitPayload { keep_blinded_messages, send_blinded_messages, @@ -158,6 +168,54 @@ impl CashuWallet { }) } + pub fn process_split_response( + &self, + blinded_messages: BlindedMessages, + promisses: Vec, + ) -> Result { + let BlindedMessages { + blinded_messages, + secrets, + rs, + amounts, + } = blinded_messages; + + println!( + "b: {:?}", + blinded_messages + .iter() + .map(|b| b.amount) + .collect::>() + ); + + println!("a: {:?}", amounts); + let secrets: Vec<_> = secrets.iter().collect(); + let mut proofs = vec![]; + + for (i, promise) in promisses.iter().enumerate() { + let a = self + .mint_keys + .amount_key(promise.amount) + .unwrap() + .to_owned(); + + let blinded_c = promise.c.clone(); + + let unblinded_sig = unblind_message(blinded_c, rs[i].clone().into(), a).unwrap(); + let proof = Proof { + id: Some(promise.id.clone()), + amount: promise.amount, + secret: secrets[i].clone(), + c: unblinded_sig, + script: None, + }; + + proofs.push(proof); + } + + Ok(proofs) + } + /// Send pub async fn send(&self, amount: Amount, proofs: Proofs) -> Result { let mut amount_available = Amount::ZERO; @@ -251,3 +309,89 @@ impl CashuWallet { Token::new(self.client.mint_url.clone(), proofs, memo).convert_to_string() } } + +#[cfg(test)] +mod tests { + + use std::collections::{HashMap, HashSet}; + + use super::*; + + use crate::client::Client; + use crate::mint::Mint; + use crate::nuts::nut04; + + #[test] + fn test_wallet() { + let mut mint = Mint::new( + "supersecretsecret", + "0/0/0/0", + HashMap::new(), + HashSet::new(), + 32, + ); + + let keys = mint.active_keyset_pubkeys(); + + 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(100)).unwrap(); + + let mint_request = nut04::MintRequest { + outputs: blinded_messages.blinded_messages.clone(), + }; + + let res = mint.process_mint_request(mint_request).unwrap(); + /* + let proofs = construct_proofs( + res.promises, + blinded_messages.rs, + blinded_messages.secrets, + &mint.active_keyset_pubkeys().keys, + ) + .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(Amount::from_sat(33), Amount::from_sat(67), proofs.clone()) + .unwrap(); + + let split_request = split.split_payload; + let split_response = mint.process_split_request(split_request).unwrap(); + let mut p = split_response.snd; + p.reverse(); + + let snd_proofs = wallet + .process_split_response(split.send_blinded_messages, p) + .unwrap(); + /* + let snd_proofs = construct_proofs( + split_response.snd, + split.send_blinded_messages.rs, + split.send_blinded_messages.secrets, + &mint.active_keyset_pubkeys().keys, + ) + .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!() + } + } +}