Browse Source

Revert "mint remove support for `NUT-06` `amount`"

This reverts commit adf3a4db8c261adef0709be36d13078835d1614b.
thesimplekid 1 year ago
parent
commit
4b4999057f
3 changed files with 151 additions and 34 deletions
  1. 34 7
      src/mint.rs
  2. 78 7
      src/nuts/nut06.rs
  3. 39 20
      src/wallet.rs

+ 34 - 7
src/mint.rs

@@ -109,17 +109,44 @@ impl Mint {
 
         let mut secrets = Vec::with_capacity(split_request.proofs.len());
         for proof in &split_request.proofs {
-            secrets.push(self.verify_proof(proof)?);
+            secrets.push(self.verify_proof(&proof)?);
             self.spent_secrets.insert(proof.secret.clone());
         }
 
-        let promises: Vec<BlindedSignature> = split_request
-            .outputs
-            .iter()
-            .map(|b| self.blind_sign(b).unwrap())
-            .collect();
+        match &split_request.amount {
+            None => {
+                let promises: Vec<BlindedSignature> = split_request
+                    .outputs
+                    .iter()
+                    .map(|b| self.blind_sign(b).unwrap())
+                    .collect();
+
+                Ok(SplitResponse::new(promises))
+            }
+            Some(amount) => {
+                if proofs_total.le(amount) {
+                    return Err(Error::Amount);
+                }
+
+                let outs_fst = (proofs_total.to_owned() - amount.to_owned()).split();
+
+                // Blinded change messages
+                let b_fst = split_request.outputs[0..outs_fst.len()].to_vec();
+                let b_snd = split_request.outputs[outs_fst.len()..].to_vec();
+                let fst: Vec<BlindedSignature> =
+                    b_fst.iter().map(|b| self.blind_sign(b).unwrap()).collect();
+                let snd: Vec<BlindedSignature> =
+                    b_snd.iter().map(|b| self.blind_sign(b).unwrap()).collect();
 
-        Ok(SplitResponse::new(promises))
+                let split_response = SplitResponse::new_from_amount(fst, snd);
+
+                if split_response.target_amount() != split_request.amount {
+                    return Err(Error::OutputOrdering);
+                }
+
+                Ok(split_response)
+            }
+        }
     }
 
     pub fn verify_proof(&self, proof: &Proof) -> Result<String, Error> {

+ 78 - 7
src/nuts/nut06.rs

@@ -20,6 +20,9 @@ pub struct SplitPayload {
 /// Split Request [NUT-06]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct SplitRequest {
+    #[deprecated(since = "0.3.0", note = "mint does not need amount")]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub amount: Option<Amount>,
     pub proofs: Proofs,
     pub outputs: Vec<BlindedMessage>,
 }
@@ -36,19 +39,87 @@ impl SplitRequest {
 /// Split Response [NUT-06]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct SplitResponse {
+    /// Promises to keep
+    #[deprecated(
+        since = "0.3.0",
+        note = "mint only response with one list of all promises"
+    )]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub fst: Option<Vec<BlindedSignature>>,
+    /// Promises to send
+    #[deprecated(
+        since = "0.3.0",
+        note = "mint only response with one list of all promises"
+    )]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub snd: Option<Vec<BlindedSignature>>,
     /// Promises
-    pub promises: Vec<BlindedSignature>,
+    pub promises: Option<Vec<BlindedSignature>>,
 }
 
 impl SplitResponse {
     pub fn new(promises: Vec<BlindedSignature>) -> SplitResponse {
-        SplitResponse { promises }
+        SplitResponse {
+            fst: None,
+            snd: None,
+            promises: Some(promises),
+        }
     }
 
-    pub fn promises_amount(&self) -> Amount {
-        self.promises
-            .iter()
-            .map(|BlindedSignature { amount, .. }| *amount)
-            .sum::<Amount>()
+    #[deprecated(
+        since = "0.3.0",
+        note = "mint only response with one list of all promises"
+    )]
+    pub fn new_from_amount(
+        fst: Vec<BlindedSignature>,
+        snd: Vec<BlindedSignature>,
+    ) -> SplitResponse {
+        Self {
+            fst: Some(fst),
+            snd: Some(snd),
+            promises: None,
+        }
+    }
+
+    #[deprecated(
+        since = "0.3.0",
+        note = "mint only response with one list of all promises"
+    )]
+    pub fn change_amount(&self) -> Option<Amount> {
+        match &self.fst {
+            Some(fst) => Some(
+                fst.iter()
+                    .map(|BlindedSignature { amount, .. }| *amount)
+                    .sum(),
+            ),
+            None => None,
+        }
+    }
+
+    #[deprecated(
+        since = "0.3.0",
+        note = "mint only response with one list of all promises"
+    )]
+    pub fn target_amount(&self) -> Option<Amount> {
+        match &self.snd {
+            Some(snd) => Some(
+                snd.iter()
+                    .map(|BlindedSignature { amount, .. }| *amount)
+                    .sum(),
+            ),
+            None => None,
+        }
+    }
+
+    pub fn promises_amount(&self) -> Option<Amount> {
+        match &self.promises {
+            Some(promises) => Some(
+                promises
+                    .iter()
+                    .map(|BlindedSignature { amount, .. }| *amount)
+                    .sum(),
+            ),
+            None => None,
+        }
     }
 }

+ 39 - 20
src/wallet.rs

@@ -1,6 +1,8 @@
 //! Cashu Wallet
 use std::str::FromStr;
 
+use log::warn;
+
 use crate::dhke::unblind_message;
 use crate::nuts::nut00::{
     mint, wallet::BlindedMessages, wallet::Token, BlindedSignature, Proof, Proofs,
@@ -107,19 +109,28 @@ impl Wallet {
                 Client::new(token.mint.as_str())?.get_keys().await?
             };
 
+            // Sum amount of all proofs
+            let amount: Amount = token.proofs.iter().map(|p| p.amount).sum();
+
             let split_payload = self.create_split(token.proofs)?;
 
             let split_response = self.client.split(split_payload.split_payload).await?;
 
-            let promises = &split_response.promises;
-            // Proof to keep
-            let p = construct_proofs(
-                promises.to_owned(),
-                split_payload.blinded_messages.rs,
-                split_payload.blinded_messages.secrets,
-                &keys,
-            )?;
-            proofs.push(p);
+            if let Some(promises) = &split_response.promises {
+                // Proof to keep
+                let p = construct_proofs(
+                    promises.to_owned(),
+                    split_payload.blinded_messages.rs,
+                    split_payload.blinded_messages.secrets,
+                    &keys,
+                )?;
+                proofs.push(p);
+            } else {
+                warn!("Response missing promises");
+                return Err(Error::CustomError(
+                    "Split response missing promises".to_string(),
+                ));
+            }
         }
         Ok(proofs.iter().flatten().cloned().collect())
     }
@@ -131,6 +142,7 @@ impl Wallet {
         let blinded_messages = BlindedMessages::random(value)?;
 
         let split_payload = SplitRequest {
+            amount: None,
             proofs,
             outputs: blinded_messages.blinded_messages.clone(),
         };
@@ -208,25 +220,32 @@ impl Wallet {
             return Ok(send_proofs);
         }
 
+        let amount_to_keep = amount_available - amount;
         let amount_to_send = amount;
 
         let split_payload = self.create_split(send_proofs.send_proofs)?;
 
         let split_response = self.client.split(split_payload.split_payload).await?;
 
-        let promises = split_response.promises;
+        // If only promises assemble proofs needed for amount
+        let keep_proofs;
+        let send_proofs;
 
-        let proofs = construct_proofs(
-            promises,
-            split_payload.blinded_messages.rs,
-            split_payload.blinded_messages.secrets,
-            &self.mint_keys,
-        )?;
+        if let Some(promises) = split_response.promises {
+            let proofs = construct_proofs(
+                promises,
+                split_payload.blinded_messages.rs,
+                split_payload.blinded_messages.secrets,
+                &self.mint_keys,
+            )?;
 
-        let split = amount_to_send.split();
+            let split = amount_to_send.split();
 
-        let keep_proofs = proofs[0..split.len()].to_vec();
-        let send_proofs = proofs[split.len()..].to_vec();
+            keep_proofs = proofs[0..split.len()].to_vec();
+            send_proofs = proofs[split.len()..].to_vec();
+        } else {
+            return Err(Error::CustomError("Invalid split response".to_string()));
+        }
 
         // println!("Send Proofs: {:#?}", send_proofs);
         // println!("Keep Proofs: {:#?}", keep_proofs);
@@ -327,7 +346,7 @@ mod tests {
         let p = split_response.promises;
 
         let snd_proofs = wallet
-            .process_split_response(split.blinded_messages, p)
+            .process_split_response(split.blinded_messages, p.unwrap())
             .unwrap();
 
         let mut error = false;