Jelajahi Sumber

refactor: rename 'BlindedMessages' to 'PreMintSecrets'

In the nuts 'BlindedMessages' is a Vec<BlindedMessage>
using it another way is confusing this avoids that.
thesimplekid 1 tahun lalu
induk
melakukan
b92be032ee

+ 1 - 1
bindings/cashu-ffi/src/cashu.udl

@@ -98,7 +98,7 @@ interface Token {
 	
 };
 
-interface BlindedMessages {
+interface PreMintSecrets {
     [Throws=CashuError, Name=random]
 	constructor(Id keyset_id, Amount amount);
     [Throws=CashuError, Name=blank]

+ 1 - 1
bindings/cashu-ffi/src/lib.rs

@@ -7,9 +7,9 @@ mod ffi {
 
     pub use crate::error::CashuError;
     pub use crate::nuts::nut00::blinded_message::BlindedMessage;
-    pub use crate::nuts::nut00::blinded_messages::BlindedMessages;
     pub use crate::nuts::nut00::blinded_signature::BlindedSignature;
     pub use crate::nuts::nut00::mint_proofs::MintProofs;
+    pub use crate::nuts::nut00::premint_secrets::PreMintSecrets;
     pub use crate::nuts::nut00::proof::mint::Proof as MintProof;
     pub use crate::nuts::nut00::proof::Proof;
     pub use crate::nuts::nut00::token::Token;

+ 0 - 86
bindings/cashu-ffi/src/nuts/nut00/blinded_messages.rs

@@ -1,86 +0,0 @@
-use std::ops::Deref;
-use std::sync::Arc;
-
-use cashu::nuts::nut00::wallet::BlindedMessages as BlindedMessagesSdk;
-
-use crate::error::Result;
-use crate::{Amount, BlindedMessage, Id, Secret, SecretKey};
-
-pub struct BlindedMessages {
-    inner: BlindedMessagesSdk,
-}
-
-impl Deref for BlindedMessages {
-    type Target = BlindedMessagesSdk;
-    fn deref(&self) -> &Self::Target {
-        &self.inner
-    }
-}
-
-impl BlindedMessages {
-    pub fn random(keyset_id: Arc<Id>, amount: Arc<Amount>) -> Result<Self> {
-        Ok(Self {
-            inner: BlindedMessagesSdk::random(
-                *keyset_id.as_ref().deref(),
-                *amount.as_ref().deref(),
-            )?,
-        })
-    }
-
-    pub fn blank(keyset_id: Arc<Id>, fee_reserve: Arc<Amount>) -> Result<Self> {
-        Ok(Self {
-            inner: BlindedMessagesSdk::blank(
-                *keyset_id.as_ref().deref(),
-                *fee_reserve.as_ref().deref(),
-            )?,
-        })
-    }
-
-    pub fn blinded_messages(&self) -> Vec<Arc<BlindedMessage>> {
-        self.inner
-            .blinded_messages
-            .clone()
-            .into_iter()
-            .map(|b| Arc::new(b.into()))
-            .collect()
-    }
-
-    pub fn secrets(&self) -> Vec<Arc<Secret>> {
-        self.inner
-            .secrets
-            .clone()
-            .into_iter()
-            .map(|s| Arc::new(s.into()))
-            .collect()
-    }
-
-    pub fn rs(&self) -> Vec<Arc<SecretKey>> {
-        self.inner
-            .rs
-            .clone()
-            .into_iter()
-            .map(|s| Arc::new(s.into()))
-            .collect()
-    }
-
-    pub fn amounts(&self) -> Vec<Arc<Amount>> {
-        self.inner
-            .amounts
-            .clone()
-            .into_iter()
-            .map(|a| Arc::new(a.into()))
-            .collect()
-    }
-}
-
-impl From<cashu::nuts::nut00::wallet::BlindedMessages> for BlindedMessages {
-    fn from(inner: cashu::nuts::nut00::wallet::BlindedMessages) -> BlindedMessages {
-        BlindedMessages { inner }
-    }
-}
-
-impl From<BlindedMessages> for cashu::nuts::nut00::wallet::BlindedMessages {
-    fn from(blinded_messages: BlindedMessages) -> cashu::nuts::nut00::wallet::BlindedMessages {
-        blinded_messages.inner
-    }
-}

+ 1 - 1
bindings/cashu-ffi/src/nuts/nut00/mod.rs

@@ -1,6 +1,6 @@
 pub mod blinded_message;
-pub mod blinded_messages;
 pub mod blinded_signature;
 pub mod mint_proofs;
+pub mod premint_secrets;
 pub mod proof;
 pub mod token;

+ 78 - 0
bindings/cashu-ffi/src/nuts/nut00/premint_secrets.rs

@@ -0,0 +1,78 @@
+use std::ops::Deref;
+use std::sync::Arc;
+
+use cashu::nuts::nut00::wallet::PreMintSecrets as PreMintSecretsSdk;
+
+use crate::error::Result;
+use crate::{Amount, BlindedMessage, Id, Secret, SecretKey};
+
+pub struct PreMintSecrets {
+    inner: PreMintSecretsSdk,
+}
+
+impl Deref for PreMintSecrets {
+    type Target = PreMintSecretsSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl PreMintSecrets {
+    pub fn random(keyset_id: Arc<Id>, amount: Arc<Amount>) -> Result<Self> {
+        Ok(Self {
+            inner: PreMintSecretsSdk::random(
+                *keyset_id.as_ref().deref(),
+                *amount.as_ref().deref(),
+            )?,
+        })
+    }
+
+    pub fn blank(keyset_id: Arc<Id>, fee_reserve: Arc<Amount>) -> Result<Self> {
+        Ok(Self {
+            inner: PreMintSecretsSdk::blank(
+                *keyset_id.as_ref().deref(),
+                *fee_reserve.as_ref().deref(),
+            )?,
+        })
+    }
+
+    pub fn blinded_messages(&self) -> Vec<Arc<BlindedMessage>> {
+        self.inner
+            .iter()
+            .map(|premint| Arc::new(premint.blinded_message.clone().into()))
+            .collect()
+    }
+
+    pub fn secrets(&self) -> Vec<Arc<Secret>> {
+        self.inner
+            .iter()
+            .map(|premint| Arc::new(premint.secret.clone().into()))
+            .collect()
+    }
+
+    pub fn rs(&self) -> Vec<Arc<SecretKey>> {
+        self.inner
+            .iter()
+            .map(|premint| Arc::new(premint.r.clone().into()))
+            .collect()
+    }
+
+    pub fn amounts(&self) -> Vec<Arc<Amount>> {
+        self.inner
+            .iter()
+            .map(|premint| Arc::new(premint.amount.into()))
+            .collect()
+    }
+}
+
+impl From<cashu::nuts::nut00::wallet::PreMintSecrets> for PreMintSecrets {
+    fn from(inner: cashu::nuts::nut00::wallet::PreMintSecrets) -> PreMintSecrets {
+        PreMintSecrets { inner }
+    }
+}
+
+impl From<PreMintSecrets> for cashu::nuts::nut00::wallet::PreMintSecrets {
+    fn from(blinded_messages: PreMintSecrets) -> cashu::nuts::nut00::wallet::PreMintSecrets {
+        blinded_messages.inner
+    }
+}

+ 9 - 10
bindings/cashu-js/src/nuts/nut00/blinded_messages.rs

@@ -1,6 +1,6 @@
 use std::ops::Deref;
 
-use cashu::nuts::nut00::wallet::BlindedMessages;
+use cashu::nuts::nut00::wallet::PreMintSecrets;
 use wasm_bindgen::prelude::*;
 
 use crate::error::{into_err, Result};
@@ -9,11 +9,11 @@ use crate::types::JsAmount;
 
 #[wasm_bindgen(js_name = BlindedMessages)]
 pub struct JsBlindedMessages {
-    inner: BlindedMessages,
+    inner: PreMintSecrets,
 }
 
 impl Deref for JsBlindedMessages {
-    type Target = BlindedMessages;
+    type Target = PreMintSecrets;
     fn deref(&self) -> &Self::Target {
         &self.inner
     }
@@ -24,15 +24,14 @@ impl JsBlindedMessages {
     #[wasm_bindgen(js_name = random)]
     pub fn random(keyset_id: JsId, amount: JsAmount) -> Result<JsBlindedMessages> {
         Ok(JsBlindedMessages {
-            inner: BlindedMessages::random(*keyset_id.deref(), *amount.deref())
-                .map_err(into_err)?,
+            inner: PreMintSecrets::random(*keyset_id.deref(), *amount.deref()).map_err(into_err)?,
         })
     }
 
     #[wasm_bindgen(js_name = blank)]
     pub fn blank(keyset_id: JsId, fee_reserve: JsAmount) -> Result<JsBlindedMessages> {
         Ok(JsBlindedMessages {
-            inner: BlindedMessages::blank(*keyset_id.deref(), *fee_reserve.deref())
+            inner: PreMintSecrets::blank(*keyset_id.deref(), *fee_reserve.deref())
                 .map_err(into_err)?,
         })
     }
@@ -40,24 +39,24 @@ impl JsBlindedMessages {
     /// Blinded Messages
     #[wasm_bindgen(getter)]
     pub fn blinded_messages(&self) -> Result<JsValue> {
-        serde_wasm_bindgen::to_value(&self.inner.blinded_messages).map_err(into_err)
+        serde_wasm_bindgen::to_value(&self.inner.blinded_messages()).map_err(into_err)
     }
 
     /// Secrets
     #[wasm_bindgen(getter)]
     pub fn secrets(&self) -> Result<JsValue> {
-        serde_wasm_bindgen::to_value(&self.inner.secrets).map_err(into_err)
+        serde_wasm_bindgen::to_value(&self.inner.secrets()).map_err(into_err)
     }
 
     /// rs
     #[wasm_bindgen(getter)]
     pub fn rs(&self) -> Result<JsValue> {
-        serde_wasm_bindgen::to_value(&self.inner.rs).map_err(into_err)
+        serde_wasm_bindgen::to_value(&self.inner.rs()).map_err(into_err)
     }
 
     /// Amounts
     #[wasm_bindgen(getter)]
     pub fn amounts(&self) -> Result<JsValue> {
-        serde_wasm_bindgen::to_value(&self.inner.amounts).map_err(into_err)
+        serde_wasm_bindgen::to_value(&self.inner.amounts()).map_err(into_err)
     }
 }

+ 2 - 2
bindings/cashu-sdk-ffi/src/cashu_sdk.udl

@@ -101,7 +101,7 @@ interface Token {
 	
 };
 
-interface BlindedMessages {
+interface PreMintSecrets {
     [Throws=CashuError, Name=random]
 	constructor(Id keyset_id, Amount amount);
     [Throws=CashuError, Name=blank]
@@ -307,7 +307,7 @@ interface Wallet {
     [Throws=CashuSdkError]
 	sequence<Proof> receive(string encoded_token);
     [Throws=CashuSdkError]
-	sequence<Proof> process_split_response(BlindedMessages blinded_messages, sequence<BlindedSignature> promises);
+	sequence<Proof> process_split_response(PreMintSecrets blinded_messages, sequence<BlindedSignature> promises);
     [Throws=CashuSdkError]
 	SendProofs send(Amount amount, sequence<Proof> proofs);
     [Throws=CashuSdkError]

+ 6 - 6
bindings/cashu-sdk-ffi/src/lib.rs

@@ -5,12 +5,12 @@ mod wallet;
 
 mod ffi {
     pub use cashu_ffi::{
-        Amount, BlindedMessage, BlindedMessages, BlindedSignature, Bolt11Invoice, CashuError,
-        CheckFeesRequest, CheckFeesResponse, CheckSpendableRequest, CheckSpendableResponse, Id,
-        InvoiceStatus, KeyPair, KeySet, KeySetInfo, KeySetResponse, Keys, KeysResponse,
-        MeltRequest, MeltResponse, MintInfo, MintKeySet, MintProof, MintProofs, MintRequest,
-        MintVersion, Nut05MeltRequest, Nut05MeltResponse, PostMintResponse, Proof, PublicKey,
-        RequestMintResponse, Secret, SecretKey, SplitRequest, SplitResponse, Token,
+        Amount, BlindedMessage, BlindedSignature, Bolt11Invoice, CashuError, CheckFeesRequest,
+        CheckFeesResponse, CheckSpendableRequest, CheckSpendableResponse, Id, InvoiceStatus,
+        KeyPair, KeySet, KeySetInfo, KeySetResponse, Keys, KeysResponse, MeltRequest, MeltResponse,
+        MintInfo, MintKeySet, MintProof, MintProofs, MintRequest, MintVersion, Nut05MeltRequest,
+        Nut05MeltResponse, PostMintResponse, PreMintSecrets, Proof, PublicKey, RequestMintResponse,
+        Secret, SecretKey, SplitRequest, SplitResponse, Token,
     };
 
     pub use crate::error::CashuSdkError;

+ 2 - 2
bindings/cashu-sdk-ffi/src/wallet.rs

@@ -2,7 +2,7 @@ use std::ops::Deref;
 use std::sync::Arc;
 
 use cashu_ffi::{
-    BlindedMessages, BlindedSignature, Bolt11Invoice, Proof, RequestMintResponse, Token,
+    BlindedSignature, Bolt11Invoice, PreMintSecrets, Proof, RequestMintResponse, Token,
 };
 use cashu_sdk::client::minreq_client::HttpClient;
 use cashu_sdk::types::ProofsStatus;
@@ -88,7 +88,7 @@ impl Wallet {
 
     pub fn process_split_response(
         &self,
-        blinded_messages: Arc<BlindedMessages>,
+        blinded_messages: Arc<PreMintSecrets>,
         promises: Vec<Arc<BlindedSignature>>,
     ) -> Result<Vec<Arc<Proof>>> {
         Ok(self

+ 4 - 4
crates/cashu-sdk/src/client/gloo_client.rs

@@ -4,8 +4,8 @@ use async_trait::async_trait;
 #[cfg(feature = "nut09")]
 use cashu::nuts::MintInfo;
 use cashu::nuts::{
-    BlindedMessage, BlindedMessages, CheckFeesRequest, CheckFeesResponse, Keys, MeltRequest,
-    MeltResponse, MintRequest, PostMintResponse, Proof, RequestMintResponse, SplitRequest,
+    BlindedMessage, CheckFeesRequest, CheckFeesResponse, Keys, MeltRequest, MeltResponse,
+    MintRequest, PostMintResponse, PreMintSecrets, Proof, RequestMintResponse, SplitRequest,
     SplitResponse, *,
 };
 #[cfg(feature = "nut07")]
@@ -89,14 +89,14 @@ impl Client for HttpClient {
     async fn post_mint(
         &self,
         mint_url: Url,
-        blinded_messages: BlindedMessages,
+        premint_secrets: PreMintSecrets,
         hash: &str,
     ) -> Result<PostMintResponse, Error> {
         let mut url = join_url(mint_url, "mint")?;
         url.query_pairs_mut().append_pair("hash", hash);
 
         let request = MintRequest {
-            outputs: blinded_messages.blinded_messages,
+            outputs: premint_secrets.blinded_messages(),
         };
 
         let res = Request::post(url.as_str())

+ 4 - 4
crates/cashu-sdk/src/client/minreq_client.rs

@@ -6,8 +6,8 @@ use async_trait::async_trait;
 #[cfg(feature = "nut09")]
 use cashu::nuts::MintInfo;
 use cashu::nuts::{
-    BlindedMessage, BlindedMessages, CheckFeesRequest, CheckFeesResponse, Keys, MeltRequest,
-    MeltResponse, MintRequest, PostMintResponse, Proof, RequestMintResponse, SplitRequest,
+    BlindedMessage, CheckFeesRequest, CheckFeesResponse, Keys, MeltRequest, MeltResponse,
+    MintRequest, PostMintResponse, PreMintSecrets, Proof, RequestMintResponse, SplitRequest,
     SplitResponse, *,
 };
 #[cfg(feature = "nut07")]
@@ -72,14 +72,14 @@ impl Client for HttpClient {
     async fn post_mint(
         &self,
         mint_url: Url,
-        blinded_messages: BlindedMessages,
+        premint_secrets: PreMintSecrets,
         hash: &str,
     ) -> Result<PostMintResponse, Error> {
         let mut url = join_url(mint_url, "mint")?;
         url.query_pairs_mut().append_pair("hash", hash);
 
         let request = MintRequest {
-            outputs: blinded_messages.blinded_messages,
+            outputs: premint_secrets.blinded_messages(),
         };
 
         let res = minreq::post(url)

+ 3 - 3
crates/cashu-sdk/src/client/mod.rs

@@ -8,8 +8,8 @@ use cashu::nuts::CheckSpendableResponse;
 #[cfg(feature = "nut09")]
 use cashu::nuts::MintInfo;
 use cashu::nuts::{
-    BlindedMessage, BlindedMessages, CheckFeesResponse, Keys, KeysetResponse, MeltResponse,
-    PostMintResponse, Proof, RequestMintResponse, SplitRequest, SplitResponse,
+    BlindedMessage, CheckFeesResponse, Keys, KeysetResponse, MeltResponse, PostMintResponse,
+    PreMintSecrets, Proof, RequestMintResponse, SplitRequest, SplitResponse,
 };
 use cashu::{utils, Amount};
 use serde::{Deserialize, Serialize};
@@ -99,7 +99,7 @@ pub trait Client {
     async fn post_mint(
         &self,
         mint_url: Url,
-        blinded_messages: BlindedMessages,
+        premint_secrets: PreMintSecrets,
         hash: &str,
     ) -> Result<PostMintResponse, Error>;
 

+ 3 - 6
crates/cashu-sdk/src/mint.rs

@@ -38,11 +38,9 @@ impl Mint {
 
         // Check that there is only one active keyset per unit
         for keyset_info in keysets_info {
-            if keyset_info.active {
-                if !active_units.insert(keyset_info.unit.clone()) {
-                    // TODO: Handle Error
-                    todo!()
-                }
+            if keyset_info.active && !active_units.insert(keyset_info.unit.clone()) {
+                // TODO: Handle Error
+                todo!()
             }
 
             let keyset = nut02::mint::KeySet::generate(
@@ -88,7 +86,6 @@ impl Mint {
         let keysets = self
             .keysets_info
             .values()
-            .into_iter()
             .map(|k| k.clone().into())
             .collect();
 

+ 26 - 32
crates/cashu-sdk/src/wallet.rs

@@ -5,7 +5,7 @@ use cashu::dhke::{construct_proofs, unblind_message};
 #[cfg(feature = "nut07")]
 use cashu::nuts::nut00::mint;
 use cashu::nuts::{
-    BlindedMessages, BlindedSignature, Keys, Proof, Proofs, RequestMintResponse, SplitPayload,
+    BlindedSignature, Keys, PreMintSecrets, Proof, Proofs, RequestMintResponse, SplitPayload,
     SplitRequest, Token,
 };
 #[cfg(feature = "nut07")]
@@ -106,21 +106,21 @@ impl<C: Client> Wallet<C> {
 
     /// Mint Proofs
     pub async fn mint(&self, amount: Amount, hash: &str) -> Result<Proofs, Error> {
-        let blinded_messages = BlindedMessages::random((&self.mint_keys).into(), amount)?;
+        let premint_secrets = PreMintSecrets::random((&self.mint_keys).into(), amount)?;
 
         let mint_res = self
             .client
             .post_mint(
                 self.mint_url.clone().try_into()?,
-                blinded_messages.clone(),
+                premint_secrets.clone(),
                 hash,
             )
             .await?;
 
         let proofs = construct_proofs(
             mint_res.promises,
-            blinded_messages.rs,
-            blinded_messages.secrets,
+            premint_secrets.rs(),
+            premint_secrets.secrets(),
             &self.mint_keys,
         )?;
 
@@ -169,8 +169,8 @@ impl<C: Client> Wallet<C> {
                 // Proof to keep
                 let p = construct_proofs(
                     promises.to_owned(),
-                    split_payload.blinded_messages.rs,
-                    split_payload.blinded_messages.secrets,
+                    split_payload.pre_mint_secrets.rs(),
+                    split_payload.pre_mint_secrets.secrets(),
                     &keys,
                 )?;
                 proofs.push(p);
@@ -183,49 +183,43 @@ impl<C: Client> Wallet<C> {
     }
 
     /// Create Split Payload
-    /// TODO: This needs to sort to avoid finer printing
     fn create_split(&self, amount: Option<Amount>, proofs: Proofs) -> Result<SplitPayload, Error> {
         // Since split is used to get the needed combination of tokens for a specific
         // amount first blinded messages are created for the amount
 
-        let blinded_messages = if let Some(amount) = amount {
-            let mut desired_messages = BlindedMessages::random((&self.mint_keys).into(), amount)?;
+        let pre_mint_secrets = if let Some(amount) = amount {
+            let mut desired_messages = PreMintSecrets::random((&self.mint_keys).into(), amount)?;
 
             let change_amount = proofs.iter().map(|p| p.amount).sum::<Amount>() - amount;
 
-            let change_messages = BlindedMessages::random((&self.mint_keys).into(), change_amount)?;
+            let change_messages = PreMintSecrets::random((&self.mint_keys).into(), change_amount)?;
+            // Combine the BlindedMessages totoalling the desired amount with change
             desired_messages.combine(change_messages);
+            // Sort the premint secrets to avoid finger printing
+            desired_messages.sort_secrets();
             desired_messages
         } else {
             let value = proofs.iter().map(|p| p.amount).sum();
 
-            BlindedMessages::random((&self.mint_keys).into(), value)?
+            PreMintSecrets::random((&self.mint_keys).into(), value)?
         };
 
-        let split_payload = SplitRequest::new(proofs, blinded_messages.blinded_messages.clone());
+        let split_payload = SplitRequest::new(proofs, pre_mint_secrets.blinded_messages());
 
         Ok(SplitPayload {
-            blinded_messages,
+            pre_mint_secrets,
             split_payload,
         })
     }
 
     pub fn process_split_response(
         &self,
-        blinded_messages: BlindedMessages,
+        blinded_messages: PreMintSecrets,
         promises: Vec<BlindedSignature>,
     ) -> Result<Proofs, Error> {
-        let BlindedMessages {
-            blinded_messages: _,
-            secrets,
-            rs,
-            amounts: _,
-        } = blinded_messages;
-
-        let secrets: Vec<_> = secrets.iter().collect();
         let mut proofs = vec![];
 
-        for (i, promise) in promises.iter().enumerate() {
+        for (promise, premint) in promises.iter().zip(blinded_messages) {
             let a = self
                 .mint_keys
                 .amount_key(promise.amount)
@@ -234,11 +228,11 @@ impl<C: Client> Wallet<C> {
 
             let blinded_c = promise.c.clone();
 
-            let unblinded_sig = unblind_message(blinded_c, rs[i].clone().into(), a).unwrap();
+            let unblinded_sig = unblind_message(blinded_c, premint.r.into(), a).unwrap();
             let proof = Proof {
                 id: promise.id,
                 amount: promise.amount,
-                secret: secrets[i].clone(),
+                secret: premint.secret,
                 c: unblinded_sig,
             };
 
@@ -273,8 +267,8 @@ impl<C: Client> Wallet<C> {
         if let Some(promises) = split_response.promises {
             let mut proofs = construct_proofs(
                 promises,
-                split_payload.blinded_messages.rs,
-                split_payload.blinded_messages.secrets,
+                split_payload.pre_mint_secrets.rs(),
+                split_payload.pre_mint_secrets.secrets(),
                 &self.mint_keys,
             )?;
 
@@ -316,22 +310,22 @@ impl<C: Client> Wallet<C> {
         proofs: Proofs,
         fee_reserve: Amount,
     ) -> Result<Melted, Error> {
-        let blinded = BlindedMessages::blank((&self.mint_keys).into(), fee_reserve)?;
+        let blinded = PreMintSecrets::blank((&self.mint_keys).into(), fee_reserve)?;
         let melt_response = self
             .client
             .post_melt(
                 self.mint_url.clone().try_into()?,
                 proofs,
                 invoice,
-                Some(blinded.blinded_messages),
+                Some(blinded.blinded_messages()),
             )
             .await?;
 
         let change_proofs = match melt_response.change {
             Some(change) => Some(construct_proofs(
                 change,
-                blinded.rs,
-                blinded.secrets,
+                blinded.rs(),
+                blinded.secrets(),
                 &self.mint_keys,
             )?),
             None => None,

+ 1 - 1
crates/cashu/src/nuts/mod.rs

@@ -13,7 +13,7 @@ pub mod nut08;
 pub mod nut09;
 
 #[cfg(feature = "wallet")]
-pub use nut00::wallet::{BlindedMessages, Token};
+pub use nut00::wallet::{PreMint, PreMintSecrets, Token};
 pub use nut00::{BlindedMessage, BlindedSignature, Proof};
 pub use nut01::{Keys, KeysResponse, PublicKey, SecretKey};
 pub use nut02::mint::KeySet as MintKeySet;

+ 114 - 33
crates/cashu/src/nuts/nut00.rs

@@ -22,6 +22,7 @@ pub struct BlindedMessage {
 
 #[cfg(feature = "wallet")]
 pub mod wallet {
+    use std::cmp::Ordering;
     use std::str::FromStr;
 
     use base64::engine::{general_purpose, GeneralPurpose};
@@ -37,25 +38,65 @@ pub mod wallet {
     use crate::url::UncheckedUrl;
     use crate::{error, Amount};
 
-    /// Blinded Messages [NUT-00]
+    #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
+    pub struct PreMint {
+        /// Blinded message
+        pub blinded_message: BlindedMessage,
+        /// Secret
+        pub secret: Secret,
+        /// R
+        pub r: SecretKey,
+        /// Amount
+        pub amount: Amount,
+    }
+
+    impl Ord for PreMint {
+        fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+            self.amount.cmp(&other.amount)
+        }
+    }
+
+    impl PartialOrd for PreMint {
+        fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+            Some(self.cmp(other))
+        }
+    }
+
     #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)]
-    pub struct BlindedMessages {
-        /// Blinded messages
-        pub blinded_messages: Vec<BlindedMessage>,
-        /// Secrets
-        pub secrets: Vec<Secret>,
-        /// Rs
-        pub rs: Vec<SecretKey>,
-        /// Amounts
-        pub amounts: Vec<Amount>,
+    pub struct PreMintSecrets {
+        secrets: Vec<PreMint>,
     }
 
-    impl BlindedMessages {
+    // Implement Iterator for PreMintSecrets
+    impl Iterator for PreMintSecrets {
+        type Item = PreMint;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            // Use the iterator of the vector
+            self.secrets.pop()
+        }
+    }
+
+    impl Ord for PreMintSecrets {
+        fn cmp(&self, other: &Self) -> Ordering {
+            self.secrets.cmp(&other.secrets)
+        }
+    }
+
+    impl PartialOrd for PreMintSecrets {
+        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+            Some(self.cmp(other))
+        }
+    }
+
+    impl PreMintSecrets {
         /// Outputs for speceifed amount with random secret
         pub fn random(keyset_id: Id, amount: Amount) -> Result<Self, wallet::Error> {
-            let mut blinded_messages = BlindedMessages::default();
+            let amount_split = amount.split();
 
-            for amount in amount.split() {
+            let mut output = Vec::with_capacity(amount_split.len());
+
+            for amount in amount_split {
                 let secret = Secret::new();
                 let (blinded, r) = blind_message(secret.as_bytes(), None)?;
 
@@ -65,19 +106,19 @@ pub mod wallet {
                     keyset_id,
                 };
 
-                blinded_messages.secrets.push(secret);
-                blinded_messages.blinded_messages.push(blinded_message);
-                blinded_messages.rs.push(r.into());
-                blinded_messages.amounts.push(amount);
+                output.push(PreMint {
+                    secret,
+                    blinded_message,
+                    r: r.into(),
+                    amount,
+                });
             }
 
-            Ok(blinded_messages)
+            Ok(PreMintSecrets { secrets: output })
         }
 
         /// Blank Outputs used for NUT-08 change
         pub fn blank(keyset_id: Id, fee_reserve: Amount) -> Result<Self, wallet::Error> {
-            let mut blinded_messages = BlindedMessages::default();
-
             let fee_reserve = bitcoin::Amount::from_sat(fee_reserve.to_sat());
 
             let count = (fee_reserve
@@ -86,6 +127,8 @@ pub mod wallet {
                 .ceil() as u64)
                 .max(1);
 
+            let mut output = Vec::with_capacity(count as usize);
+
             for _i in 0..count {
                 let secret = Secret::new();
                 let (blinded, r) = blind_message(secret.as_bytes(), None)?;
@@ -96,20 +139,58 @@ pub mod wallet {
                     keyset_id,
                 };
 
-                blinded_messages.secrets.push(secret);
-                blinded_messages.blinded_messages.push(blinded_message);
-                blinded_messages.rs.push(r.into());
-                blinded_messages.amounts.push(Amount::ZERO);
+                output.push(PreMint {
+                    secret,
+                    blinded_message,
+                    r: r.into(),
+                    amount: Amount::ZERO,
+                })
             }
 
-            Ok(blinded_messages)
+            Ok(PreMintSecrets { secrets: output })
+        }
+
+        pub fn iter(&self) -> impl Iterator<Item = &PreMint> {
+            self.secrets.iter()
+        }
+
+        pub fn len(&self) -> usize {
+            self.secrets.len()
+        }
+
+        pub fn is_empty(&self) -> bool {
+            self.secrets.is_empty()
+        }
+
+        pub fn total_amount(&self) -> Amount {
+            self.secrets
+                .iter()
+                .map(|PreMint { amount, .. }| *amount)
+                .sum()
+        }
+
+        pub fn blinded_messages(&self) -> Vec<BlindedMessage> {
+            self.iter().map(|pm| pm.blinded_message.clone()).collect()
+        }
+
+        pub fn secrets(&self) -> Vec<Secret> {
+            self.iter().map(|pm| pm.secret.clone()).collect()
+        }
+
+        pub fn rs(&self) -> Vec<SecretKey> {
+            self.iter().map(|pm| pm.r.clone()).collect()
+        }
+
+        pub fn amounts(&self) -> Vec<Amount> {
+            self.iter().map(|pm| pm.amount).collect()
         }
 
         pub fn combine(&mut self, mut other: Self) {
-            self.blinded_messages.append(&mut other.blinded_messages);
-            self.secrets.append(&mut other.secrets);
-            self.rs.append(&mut other.rs);
-            self.amounts.append(&mut other.amounts);
+            self.secrets.append(&mut other.secrets)
+        }
+
+        pub fn sort_secrets(&mut self) {
+            self.secrets.sort();
         }
     }
 
@@ -335,11 +416,11 @@ mod tests {
     #[test]
     fn test_blank_blinded_messages() {
         // TODO: Need to update id to new type in proof
-        let b = BlindedMessages::blank(Id::from_str("").unwrap(), Amount::from_sat(1000)).unwrap();
-        assert_eq!(b.blinded_messages.len(), 10);
+        let b = PreMintSecrets::blank(Id::from_str("").unwrap(), Amount::from_sat(1000)).unwrap();
+        assert_eq!(b.len(), 10);
 
         // TODO: Need to update id to new type in proof
-        let b = BlindedMessages::blank(Id::from_str("").unwrap(), Amount::from_sat(1)).unwrap();
-        assert_eq!(b.blinded_messages.len(), 1);
+        let b = PreMintSecrets::blank(Id::from_str("").unwrap(), Amount::from_sat(1)).unwrap();
+        assert_eq!(b.len(), 1);
     }
 }

+ 2 - 2
crates/cashu/src/nuts/nut03.rs

@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
 
 use super::nut00::BlindedSignature;
 #[cfg(feature = "wallet")]
-use crate::nuts::BlindedMessages;
+use crate::nuts::PreMintSecrets;
 use crate::nuts::{BlindedMessage, Proofs};
 use crate::Amount;
 pub use crate::Bolt11Invoice;
@@ -22,7 +22,7 @@ pub struct RequestMintResponse {
 #[cfg(feature = "wallet")]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
 pub struct SplitPayload {
-    pub blinded_messages: BlindedMessages,
+    pub pre_mint_secrets: PreMintSecrets,
     pub split_payload: SplitRequest,
 }
 

+ 2 - 2
crates/cashu/src/nuts/nut06.rs

@@ -4,14 +4,14 @@ use serde::{Deserialize, Serialize};
 
 use super::nut00::BlindedSignature;
 #[cfg(feature = "wallet")]
-use crate::nuts::BlindedMessages;
+use crate::nuts::PreMintSecrets;
 use crate::nuts::{BlindedMessage, Proofs};
 use crate::Amount;
 
 #[cfg(feature = "wallet")]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
 pub struct SplitPayload {
-    pub blinded_messages: BlindedMessages,
+    pub pre_mint_secrets: PreMintSecrets,
     pub split_payload: SplitRequest,
 }