Browse Source

Add Proofs trait to consolidate aggregate fns

ok300 4 months ago
parent
commit
f6533a08de

+ 2 - 4
crates/cdk-redb/src/mint/mod.rs

@@ -10,6 +10,7 @@ use async_trait::async_trait;
 use cdk::cdk_database::MintDatabase;
 use cdk::dhke::hash_to_curve;
 use cdk::mint::{MintKeySetInfo, MintQuote};
+use cdk::nuts::nut00::ProofsMethods;
 use cdk::nuts::{
     BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof,
     Proofs, PublicKey, State,
@@ -603,10 +604,7 @@ impl MintDatabase for MintRedbDatabase {
             .filter(|p| &p.keyset_id == keyset_id)
             .collect::<Proofs>();
 
-        let proof_ys = proofs_for_id
-            .iter()
-            .map(|p| p.y())
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let proof_ys = proofs_for_id.ys()?;
 
         assert_eq!(proofs_for_id.len(), proof_ys.len());
 

+ 2 - 4
crates/cdk-sqlite/src/mint/mod.rs

@@ -10,6 +10,7 @@ use bitcoin::bip32::DerivationPath;
 use cdk::cdk_database::{self, MintDatabase};
 use cdk::mint::{MintKeySetInfo, MintQuote};
 use cdk::mint_url::MintUrl;
+use cdk::nuts::nut00::ProofsMethods;
 use cdk::nuts::nut05::QuoteState;
 use cdk::nuts::{
     BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState,
@@ -838,10 +839,7 @@ WHERE quote_id=?;
                     .map(sqlite_row_to_proof)
                     .collect::<Result<Vec<Proof>, _>>()?;
 
-                proofs
-                    .iter()
-                    .map(|p| p.y())
-                    .collect::<Result<Vec<PublicKey>, _>>()?
+                proofs.ys()?
             }
             Err(err) => match err {
                 sqlx::Error::RowNotFound => {

+ 2 - 4
crates/cdk/src/cdk_database/mint_memory.rs

@@ -9,6 +9,7 @@ use tokio::sync::{Mutex, RwLock};
 use super::{Error, MintDatabase};
 use crate::dhke::hash_to_curve;
 use crate::mint::{self, MintKeySetInfo, MintQuote};
+use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::nut07::State;
 use crate::nuts::{
     nut07, BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState,
@@ -346,10 +347,7 @@ impl MintDatabase for MintMemoryDatabase {
             .cloned()
             .collect();
 
-        let proof_ys = proofs_for_id
-            .iter()
-            .map(|p| p.y())
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let proof_ys = proofs_for_id.ys()?;
 
         assert_eq!(proofs_for_id.len(), proof_ys.len());
 

+ 4 - 16
crates/cdk/src/mint/melt.rs

@@ -8,7 +8,7 @@ use tracing::instrument;
 use crate::cdk_lightning;
 use crate::cdk_lightning::MintLightning;
 use crate::cdk_lightning::PayInvoiceResponse;
-use crate::dhke::hash_to_curve;
+use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::nut11::enforce_sig_flag;
 use crate::nuts::nut11::EnforceSigFlag;
 use crate::{
@@ -264,11 +264,7 @@ impl Mint {
             }
         }
 
-        let ys = melt_request
-            .inputs
-            .iter()
-            .map(|p| hash_to_curve(&p.secret.to_bytes()))
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let ys = melt_request.inputs.ys()?;
 
         // Ensure proofs are unique and not being double spent
         if melt_request.inputs.len() != ys.iter().collect::<HashSet<_>>().len() {
@@ -374,11 +370,7 @@ impl Mint {
     /// quote should be unpaid
     #[instrument(skip_all)]
     pub async fn process_unpaid_melt(&self, melt_request: &MeltBolt11Request) -> Result<(), Error> {
-        let input_ys = melt_request
-            .inputs
-            .iter()
-            .map(|p| hash_to_curve(&p.secret.to_bytes()))
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let input_ys = melt_request.inputs.ys()?;
 
         self.localstore
             .update_proofs_states(&input_ys, State::Unspent)
@@ -615,11 +607,7 @@ impl Mint {
             .await?
             .ok_or(Error::UnknownQuote)?;
 
-        let input_ys = melt_request
-            .inputs
-            .iter()
-            .map(|p| hash_to_curve(&p.secret.to_bytes()))
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let input_ys = melt_request.inputs.ys()?;
 
         self.localstore
             .update_proofs_states(&input_ys, State::Spent)

+ 2 - 6
crates/cdk/src/mint/swap.rs

@@ -2,7 +2,7 @@ use std::collections::HashSet;
 
 use tracing::instrument;
 
-use crate::dhke::hash_to_curve;
+use crate::nuts::nut00::ProofsMethods;
 use crate::Error;
 
 use super::nut11::{enforce_sig_flag, EnforceSigFlag};
@@ -59,11 +59,7 @@ impl Mint {
 
         let proof_count = swap_request.inputs.len();
 
-        let input_ys = swap_request
-            .inputs
-            .iter()
-            .map(|p| hash_to_curve(&p.secret.to_bytes()))
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let input_ys = swap_request.inputs.ys()?;
 
         self.localstore
             .add_proofs(swap_request.inputs.clone(), None)

+ 22 - 0
crates/cdk/src/nuts/nut00/mod.rs

@@ -29,6 +29,28 @@ pub use token::{Token, TokenV3, TokenV4};
 /// List of [Proof]
 pub type Proofs = Vec<Proof>;
 
+/// Utility methods for [Proofs]
+pub trait ProofsMethods {
+    /// Try to sum up the amounts of all [Proof]s
+    fn total_amount(&self) -> Result<Amount, Error>;
+
+    /// Try to fetch the pubkeys of all [Proof]s
+    fn ys(&self) -> Result<Vec<PublicKey>, Error>;
+}
+
+impl ProofsMethods for Proofs {
+    fn total_amount(&self) -> Result<Amount, Error> {
+        Amount::try_sum(self.iter().map(|p| p.amount)).map_err(Into::into)
+    }
+
+    fn ys(&self) -> Result<Vec<PublicKey>, Error> {
+        self.iter()
+            .map(|p| p.y())
+            .collect::<Result<Vec<PublicKey>, _>>()
+            .map_err(Into::into)
+    }
+}
+
 /// NUT00 Error
 #[derive(Debug, Error)]
 pub enum Error {

+ 2 - 1
crates/cdk/src/nuts/nut00/token.rs

@@ -13,6 +13,7 @@ use url::Url;
 
 use super::{Error, Proof, ProofV4, Proofs};
 use crate::mint_url::MintUrl;
+use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::{CurrencyUnit, Id};
 use crate::Amount;
 
@@ -211,7 +212,7 @@ impl TokenV3 {
         Ok(Amount::try_sum(
             self.token
                 .iter()
-                .map(|t| Amount::try_sum(t.proofs.iter().map(|p| p.amount)))
+                .map(|t| t.proofs.total_amount())
                 .collect::<Result<Vec<Amount>, _>>()?,
         )?)
     }

+ 3 - 2
crates/cdk/src/types.rs

@@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
 
 use crate::error::Error;
 use crate::mint_url::MintUrl;
+use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::{
     CurrencyUnit, MeltQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SpendingConditions,
     State,
@@ -34,9 +35,9 @@ impl Melted {
         proofs: Proofs,
         change_proofs: Option<Proofs>,
     ) -> Result<Self, Error> {
-        let proofs_amount = Amount::try_sum(proofs.iter().map(|p| p.amount))?;
+        let proofs_amount = proofs.total_amount()?;
         let change_amount = match &change_proofs {
-            Some(change_proofs) => Amount::try_sum(change_proofs.iter().map(|p| p.amount))?,
+            Some(change_proofs) => change_proofs.total_amount()?,
             None => Amount::ZERO,
         };
         let fee_paid = proofs_amount

+ 6 - 11
crates/cdk/src/wallet/melt.rs

@@ -3,9 +3,10 @@ use std::str::FromStr;
 use lightning_invoice::Bolt11Invoice;
 use tracing::instrument;
 
+use crate::nuts::nut00::ProofsMethods;
 use crate::{
     dhke::construct_proofs,
-    nuts::{CurrencyUnit, MeltQuoteBolt11Response, PreMintSecrets, Proofs, PublicKey, State},
+    nuts::{CurrencyUnit, MeltQuoteBolt11Response, PreMintSecrets, Proofs, State},
     types::{Melted, ProofInfo},
     util::unix_time,
     Amount, Error, Wallet,
@@ -121,15 +122,12 @@ impl Wallet {
             return Err(Error::UnknownQuote);
         };
 
-        let proofs_total = Amount::try_sum(proofs.iter().map(|p| p.amount))?;
+        let proofs_total = proofs.total_amount()?;
         if proofs_total < quote_info.amount + quote_info.fee_reserve {
             return Err(Error::InsufficientFunds);
         }
 
-        let ys = proofs
-            .iter()
-            .map(|p| p.y())
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let ys = proofs.ys()?;
         self.localstore.set_pending_proofs(ys).await?;
 
         let active_keyset_id = self.get_active_mint_keyset().await?.id;
@@ -213,7 +211,7 @@ impl Wallet {
             Some(change_proofs) => {
                 tracing::debug!(
                     "Change amount returned from melt: {}",
-                    Amount::try_sum(change_proofs.iter().map(|p| p.amount))?
+                    change_proofs.total_amount()?
                 );
 
                 // Update counter for keyset
@@ -238,10 +236,7 @@ impl Wallet {
 
         self.localstore.remove_melt_quote(&quote_info.id).await?;
 
-        let deleted_ys = proofs
-            .iter()
-            .map(|p| p.y())
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let deleted_ys = proofs.ys()?;
         self.localstore
             .update_proofs(change_proof_infos, deleted_ys)
             .await?;

+ 2 - 1
crates/cdk/src/wallet/mint.rs

@@ -1,6 +1,7 @@
 use tracing::instrument;
 
 use super::MintQuote;
+use crate::nuts::nut00::ProofsMethods;
 use crate::{
     amount::SplitTarget,
     dhke::construct_proofs,
@@ -242,7 +243,7 @@ impl Wallet {
             &keys,
         )?;
 
-        let minted_amount = Amount::try_sum(proofs.iter().map(|p| p.amount))?;
+        let minted_amount = proofs.total_amount()?;
 
         // Remove filled quote from store
         self.localstore.remove_mint_quote(&quote_info.id).await?;

+ 2 - 1
crates/cdk/src/wallet/mod.rs

@@ -35,6 +35,7 @@ mod swap;
 pub mod types;
 pub mod util;
 
+use crate::nuts::nut00::ProofsMethods;
 pub use multi_mint_wallet::MultiMintWallet;
 pub use types::{MeltQuote, MintQuote, SendKind};
 
@@ -327,7 +328,7 @@ impl Wallet {
                     .cloned()
                     .collect();
 
-                restored_value += Amount::try_sum(unspent_proofs.iter().map(|p| p.amount))?;
+                restored_value += unspent_proofs.total_amount()?;
 
                 let unspent_proofs = unspent_proofs
                     .into_iter()

+ 7 - 18
crates/cdk/src/wallet/proofs.rs

@@ -2,9 +2,9 @@ use std::collections::HashSet;
 
 use tracing::instrument;
 
+use crate::nuts::nut00::ProofsMethods;
 use crate::{
     amount::SplitTarget,
-    dhke::hash_to_curve,
     nuts::{Proof, ProofState, Proofs, PublicKey, State},
     types::ProofInfo,
     Amount, Error, Wallet,
@@ -73,11 +73,7 @@ impl Wallet {
     /// Checks the stats of [`Proofs`] swapping for a new [`Proof`] if unspent
     #[instrument(skip(self, proofs))]
     pub async fn reclaim_unspent(&self, proofs: Proofs) -> Result<(), Error> {
-        let proof_ys = proofs
-            .iter()
-            // Find Y for the secret
-            .map(|p| hash_to_curve(p.secret.as_bytes()))
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let proof_ys = proofs.ys()?;
 
         let spendable = self
             .client
@@ -102,14 +98,7 @@ impl Wallet {
     pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> {
         let spendable = self
             .client
-            .post_check_state(
-                self.mint_url.clone().try_into()?,
-                proofs
-                    .iter()
-                    // Find Y for the secret
-                    .map(|p| hash_to_curve(p.secret.as_bytes()))
-                    .collect::<Result<Vec<PublicKey>, _>>()?,
-            )
+            .post_check_state(self.mint_url.clone().try_into()?, proofs.ys()?)
             .await?;
         let spent_ys: Vec<_> = spendable
             .states
@@ -186,7 +175,7 @@ impl Wallet {
     ) -> Result<Proofs, Error> {
         // TODO: Check all proofs are same unit
 
-        if Amount::try_sum(proofs.iter().map(|p| p.amount))? < amount {
+        if proofs.total_amount()? < amount {
             return Err(Error::InsufficientFunds);
         }
 
@@ -226,7 +215,7 @@ impl Wallet {
             }
 
             remaining_amount = amount.checked_add(fees).ok_or(Error::AmountOverflow)?
-                - Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
+                - selected_proofs.total_amount()?;
             (proofs_larger, proofs_smaller) = proofs_smaller
                 .into_iter()
                 .skip(1)
@@ -262,7 +251,7 @@ impl Wallet {
 
         for inactive_proof in inactive_proofs {
             selected_proofs.push(inactive_proof);
-            let selected_total = Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
+            let selected_total = selected_proofs.total_amount()?;
             let fees = self.get_proofs_fee(&selected_proofs).await?;
 
             if selected_total >= amount + fees {
@@ -274,7 +263,7 @@ impl Wallet {
 
         for active_proof in active_proofs {
             selected_proofs.push(active_proof);
-            let selected_total = Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
+            let selected_total = selected_proofs.total_amount()?;
             let fees = self.get_proofs_fee(&selected_proofs).await?;
 
             if selected_total >= amount + fees {

+ 2 - 1
crates/cdk/src/wallet/receive.rs

@@ -4,6 +4,7 @@ use bitcoin::hashes::Hash;
 use bitcoin::{hashes::sha256::Hash as Sha256Hash, XOnlyPublicKey};
 use tracing::instrument;
 
+use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::nut10::Kind;
 use crate::nuts::{Conditions, Token};
 use crate::{
@@ -148,7 +149,7 @@ impl Wallet {
             .increment_keyset_counter(&active_keyset_id, recv_proofs.len() as u32)
             .await?;
 
-        let total_amount = Amount::try_sum(recv_proofs.iter().map(|p| p.amount))?;
+        let total_amount = recv_proofs.total_amount()?;
 
         let recv_proof_infos = recv_proofs
             .into_iter()

+ 7 - 13
crates/cdk/src/wallet/send.rs

@@ -1,8 +1,9 @@
 use tracing::instrument;
 
+use crate::nuts::nut00::ProofsMethods;
 use crate::{
     amount::SplitTarget,
-    nuts::{Proofs, PublicKey, SpendingConditions, State, Token},
+    nuts::{Proofs, SpendingConditions, State, Token},
     Amount, Error, Wallet,
 };
 
@@ -12,10 +13,7 @@ impl Wallet {
     /// Send specific proofs
     #[instrument(skip(self))]
     pub async fn send_proofs(&self, memo: Option<String>, proofs: Proofs) -> Result<Token, Error> {
-        let ys = proofs
-            .iter()
-            .map(|p| p.y())
-            .collect::<Result<Vec<PublicKey>, _>>()?;
+        let ys = proofs.ys()?;
         self.localstore.reserve_proofs(ys).await?;
 
         Ok(Token::new(
@@ -114,8 +112,7 @@ impl Wallet {
         let send_proofs: Proofs = match (send_kind, selected, conditions.clone()) {
             // Handle exact matches offline
             (SendKind::OfflineExact, Ok(selected_proofs), _) => {
-                let selected_proofs_amount =
-                    Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
+                let selected_proofs_amount = selected_proofs.total_amount()?;
 
                 let amount_to_send = match include_fees {
                     true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -131,8 +128,7 @@ impl Wallet {
 
             // Handle exact matches
             (SendKind::OnlineExact, Ok(selected_proofs), _) => {
-                let selected_proofs_amount =
-                    Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
+                let selected_proofs_amount = selected_proofs.total_amount()?;
 
                 let amount_to_send = match include_fees {
                     true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -152,8 +148,7 @@ impl Wallet {
 
             // Handle offline tolerance
             (SendKind::OfflineTolerance(tolerance), Ok(selected_proofs), _) => {
-                let selected_proofs_amount =
-                    Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
+                let selected_proofs_amount = selected_proofs.total_amount()?;
 
                 let amount_to_send = match include_fees {
                     true => amount + self.get_proofs_fee(&selected_proofs).await?,
@@ -178,8 +173,7 @@ impl Wallet {
 
             // Handle online tolerance with successful selection
             (SendKind::OnlineTolerance(tolerance), Ok(selected_proofs), _) => {
-                let selected_proofs_amount =
-                    Amount::try_sum(selected_proofs.iter().map(|p| p.amount))?;
+                let selected_proofs_amount = selected_proofs.total_amount()?;
                 let amount_to_send = match include_fees {
                     true => amount + self.get_proofs_fee(&selected_proofs).await?,
                     false => amount,

+ 5 - 5
crates/cdk/src/wallet/swap.rs

@@ -2,6 +2,7 @@ use tracing::instrument;
 
 use crate::amount::SplitTarget;
 use crate::dhke::construct_proofs;
+use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::nut10;
 use crate::nuts::PreMintSecrets;
 use crate::nuts::PreSwap;
@@ -85,8 +86,7 @@ impl Wallet {
                         let mut proofs_to_keep = Vec::new();
 
                         for proof in all_proofs {
-                            let proofs_to_send_amount =
-                                Amount::try_sum(proofs_to_send.iter().map(|p| p.amount))?;
+                            let proofs_to_send_amount = proofs_to_send.total_amount()?;
                             if proof.amount + proofs_to_send_amount <= amount + pre_swap.fee {
                                 proofs_to_send.push(proof);
                             } else {
@@ -98,7 +98,7 @@ impl Wallet {
                     }
                 };
 
-                let send_amount = Amount::try_sum(proofs_to_send.iter().map(|p| p.amount))?;
+                let send_amount = proofs_to_send.total_amount()?;
 
                 if send_amount.ne(&(amount + pre_swap.fee)) {
                     tracing::warn!(
@@ -199,9 +199,9 @@ impl Wallet {
         let active_keyset_id = self.get_active_mint_keyset().await?.id;
 
         // Desired amount is either amount passed or value of all proof
-        let proofs_total = Amount::try_sum(proofs.iter().map(|p| p.amount))?;
+        let proofs_total = proofs.total_amount()?;
 
-        let ys: Vec<PublicKey> = proofs.iter().map(|p| p.y()).collect::<Result<_, _>>()?;
+        let ys: Vec<PublicKey> = proofs.ys()?;
         self.localstore.set_pending_proofs(ys).await?;
 
         let fee = self.get_proofs_fee(&proofs).await?;