Переглянути джерело

refactor: reduce number of features

Features for NUTs without extra deps expand the
feature matrix without much benefit.
thesimplekid 1 рік тому
батько
коміт
e5a531dca1

+ 2 - 0
.github/workflows/ci.yml

@@ -30,8 +30,10 @@ jobs:
             -p cashu --no-default-features,
             -p cashu --no-default-features --features wallet,
             -p cashu --no-default-features --features mint,
+            -p cashu --no-default-features --features all-nuts,
             -p cashu-sdk,
             -p cashu-sdk --no-default-features,
+            -p cashu-sdk --no-default-features --features all-nuts,
           ]
     steps:
     - name: Checkout

+ 1 - 7
crates/cashu-sdk/Cargo.toml

@@ -14,13 +14,7 @@ default = ["mint", "wallet", "all-nuts", "redb"]
 mint = ["cashu/mint"]
 wallet = ["cashu/wallet", "dep:minreq"]
 gloo = ["dep:gloo"]
-all-nuts = ["nut07", "nut08", "nut09", "nut10", "nut11", "nut12", "nut13"]
-nut07 = ["cashu/nut07"]
-nut08 = ["cashu/nut08"]
-nut09 = ["cashu/nut07", "cashu/nut09"]
-nut10 = ["cashu/nut10"]
-nut11 = ["cashu/nut11"]
-nut12 = ["cashu/nut12"]
+all-nuts = ["nut13"]
 nut13 = ["cashu/nut13"]
 redb = ["dep:redb"]
 

+ 19 - 25
crates/cashu-sdk/src/client/minreq_client.rs

@@ -2,18 +2,14 @@
 
 use async_trait::async_trait;
 use cashu::error::ErrorResponse;
-#[cfg(feature = "nut09")]
 use cashu::nuts::nut09::{RestoreRequest, RestoreResponse};
-#[cfg(feature = "nut07")]
-use cashu::nuts::PublicKey;
 use cashu::nuts::{
-    BlindedMessage, CurrencyUnit, Id, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request,
-    MeltBolt11Response, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request,
-    MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, PreMintSecrets,
-    Proof, SwapRequest, SwapResponse,
+    BlindedMessage, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysResponse,
+    KeysetResponse, MeltBolt11Request, MeltBolt11Response, MeltQuoteBolt11Request,
+    MeltQuoteBolt11Response, MintBolt11Request, MintBolt11Response, MintInfo,
+    MintQuoteBolt11Request, MintQuoteBolt11Response, PreMintSecrets, Proof, PublicKey, SwapRequest,
+    SwapResponse,
 };
-#[cfg(feature = "nut07")]
-use cashu::nuts::{CheckStateRequest, CheckStateResponse};
 use cashu::{Amount, Bolt11Invoice};
 use serde_json::Value;
 use tracing::warn;
@@ -186,8 +182,21 @@ impl Client for HttpClient {
         }
     }
 
+    /// Get Mint Info [NUT-06]
+    async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error> {
+        let url = join_url(mint_url, &["v1", "info"])?;
+
+        let res = minreq::get(url).send()?.json::<Value>()?;
+
+        let response: Result<MintInfo, serde_json::Error> = serde_json::from_value(res.clone());
+
+        match response {
+            Ok(res) => Ok(res),
+            Err(_) => Err(ErrorResponse::from_json(&res.to_string())?.into()),
+        }
+    }
+
     /// Spendable check [NUT-07]
-    #[cfg(feature = "nut07")]
     async fn post_check_state(
         &self,
         mint_url: Url,
@@ -210,21 +219,6 @@ impl Client for HttpClient {
         }
     }
 
-    /// Get Mint Info [NUT-09]
-    async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error> {
-        let url = join_url(mint_url, &["v1", "info"])?;
-
-        let res = minreq::get(url).send()?.json::<Value>()?;
-
-        let response: Result<MintInfo, serde_json::Error> = serde_json::from_value(res.clone());
-
-        match response {
-            Ok(res) => Ok(res),
-            Err(_) => Err(ErrorResponse::from_json(&res.to_string())?.into()),
-        }
-    }
-
-    #[cfg(feature = "nut09")]
     async fn post_restore(
         &self,
         mint_url: Url,

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

@@ -2,16 +2,11 @@
 
 use async_trait::async_trait;
 use cashu::error::ErrorResponse;
-#[cfg(feature = "nut09")]
 use cashu::nuts::nut09::{RestoreRequest, RestoreResponse};
-#[cfg(feature = "nut07")]
-use cashu::nuts::CheckStateResponse;
-#[cfg(feature = "nut07")]
-use cashu::nuts::PublicKey;
 use cashu::nuts::{
-    BlindedMessage, CurrencyUnit, Id, KeySet, KeysetResponse, MeltBolt11Response,
-    MeltQuoteBolt11Response, MintBolt11Response, MintInfo, MintQuoteBolt11Response, PreMintSecrets,
-    Proof, SwapRequest, SwapResponse,
+    BlindedMessage, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysetResponse,
+    MeltBolt11Response, MeltQuoteBolt11Response, MintBolt11Response, MintInfo,
+    MintQuoteBolt11Response, PreMintSecrets, Proof, PublicKey, SwapRequest, SwapResponse,
 };
 use cashu::Amount;
 use thiserror::Error;
@@ -105,7 +100,6 @@ pub trait Client {
         split_request: SwapRequest,
     ) -> Result<SwapResponse, Error>;
 
-    #[cfg(feature = "nut07")]
     async fn post_check_state(
         &self,
         mint_url: Url,
@@ -114,7 +108,6 @@ pub trait Client {
 
     async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error>;
 
-    #[cfg(feature = "nut09")]
     async fn post_restore(
         &self,
         mint_url: Url,

+ 9 - 65
crates/cashu-sdk/src/mint/mod.rs

@@ -3,16 +3,11 @@ use std::sync::Arc;
 
 use cashu::dhke::{hash_to_curve, sign_message, verify_message};
 use cashu::error::ErrorResponse;
-#[cfg(feature = "nut07")]
 use cashu::nuts::nut07::{ProofState, State};
 use cashu::nuts::{
-    BlindSignature, BlindedMessage, MeltBolt11Request, MeltBolt11Response, Proof, SwapRequest,
-    SwapResponse, *,
+    BlindSignature, BlindedMessage, CheckStateRequest, CheckStateResponse, MeltBolt11Request,
+    MeltBolt11Response, Proof, RestoreRequest, RestoreResponse, SwapRequest, SwapResponse, *,
 };
-#[cfg(feature = "nut07")]
-use cashu::nuts::{CheckStateRequest, CheckStateResponse};
-#[cfg(feature = "nut09")]
-use cashu::nuts::{RestoreRequest, RestoreResponse};
 use cashu::types::{MeltQuote, MintQuote};
 use cashu::Amount;
 use http::StatusCode;
@@ -357,26 +352,13 @@ impl Mint {
 
         let c = sign_message(&key_pair.secret_key, b)?;
 
-        let blinded_signature;
-        #[cfg(not(feature = "nut12"))]
-        {
-            blinded_signature = BlindSignature {
-                amount: *amount,
-                c: c.into(),
-                keyset_id: keyset.id,
-            };
-        }
-
-        #[cfg(feature = "nut12")]
-        {
-            blinded_signature = BlindSignature::new(
-                *amount,
-                c,
-                keyset.id,
-                &blinded_message.b,
-                key_pair.secret_key.clone(),
-            )?;
-        }
+        let blinded_signature = BlindSignature::new(
+            *amount,
+            c,
+            keyset.id,
+            &blinded_message.b,
+            key_pair.secret_key.clone(),
+        )?;
 
         Ok(blinded_signature)
     }
@@ -475,42 +457,6 @@ impl Mint {
         Ok(SwapResponse::new(promises))
     }
 
-    #[cfg(not(feature = "nut11"))]
-    async fn verify_proof(&self, proof: &Proof) -> Result<(), Error> {
-        let y = hash_to_curve(&proof.secret.to_bytes()?)?;
-        if self.localstore.get_spent_proof_by_hash(&y).await?.is_some() {
-            return Err(Error::TokenSpent);
-        }
-
-        if self
-            .localstore
-            .get_pending_proof_by_hash(&y)
-            .await?
-            .is_some()
-        {
-            return Err(Error::TokenPending);
-        }
-
-        let keyset = self
-            .localstore
-            .get_keyset(&proof.keyset_id)
-            .await?
-            .ok_or(Error::UnknownKeySet)?;
-
-        let Some(keypair) = keyset.keys.0.get(&proof.amount) else {
-            return Err(Error::AmountKey);
-        };
-
-        verify_message(
-            keypair.secret_key.clone().into(),
-            proof.c.clone().into(),
-            &proof.secret,
-        )?;
-
-        Ok(())
-    }
-
-    #[cfg(feature = "nut11")]
     async fn verify_proof(&self, proof: &Proof) -> Result<(), Error> {
         // Check if secret is a nut10 secret with conditions
         if let Ok(secret) =
@@ -549,7 +495,6 @@ impl Mint {
         Ok(())
     }
 
-    #[cfg(feature = "nut07")]
     pub async fn check_state(
         &self,
         check_state: &CheckStateRequest,
@@ -750,7 +695,6 @@ impl Mint {
         Ok(self.localstore.get_mint_info().await?)
     }
 
-    #[cfg(feature = "nut09")]
     pub async fn restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error> {
         let output_len = request.outputs.len();
 

+ 2 - 14
crates/cashu-sdk/src/wallet/mod.rs

@@ -6,17 +6,12 @@ use std::sync::Arc;
 
 use bip39::Mnemonic;
 use cashu::dhke::{construct_proofs, unblind_message};
-#[cfg(feature = "nut07")]
-use cashu::nuts::nut07::ProofState;
-use cashu::nuts::nut07::State;
-#[cfg(feature = "nut09")]
+use cashu::nuts::nut07::{ProofState, State};
 use cashu::nuts::nut09::RestoreRequest;
 use cashu::nuts::nut11::SigningKey;
-#[cfg(feature = "nut07")]
-use cashu::nuts::PublicKey;
 use cashu::nuts::{
     BlindSignature, CurrencyUnit, Id, KeySet, KeySetInfo, Keys, MintInfo, P2PKConditions,
-    PreMintSecrets, PreSwap, Proof, Proofs, SigFlag, SwapRequest, Token,
+    PreMintSecrets, PreSwap, Proof, Proofs, PublicKey, SigFlag, SwapRequest, Token,
 };
 use cashu::types::{MeltQuote, Melted, MintQuote};
 use cashu::url::UncheckedUrl;
@@ -187,7 +182,6 @@ impl Wallet {
     }
 
     /// Check if a proof is spent
-    #[cfg(feature = "nut07")]
     pub async fn check_proofs_spent(
         &self,
         mint_url: UncheckedUrl,
@@ -361,7 +355,6 @@ impl Wallet {
         let keys = self.get_keyset_keys(&mint_url, active_keyset_id).await?;
 
         // Verify the signature DLEQ is valid
-        #[cfg(feature = "nut12")]
         {
             for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
                 let keys = self.get_keyset_keys(&mint_url, sig.keyset_id).await?;
@@ -408,7 +401,6 @@ impl Wallet {
 
         // Verify the signature DLEQ is valid
         // Verify that all proofs in the token have a vlid DLEQ proof if one is supplied
-        #[cfg(feature = "nut12")]
         {
             for mint_proof in &token_data.token {
                 let mint_url = &mint_proof.mint;
@@ -570,7 +562,6 @@ impl Wallet {
 
         for (promise, premint) in promises.iter().zip(blinded_messages) {
             // Verify the signature DLEQ is valid
-            #[cfg(feature = "nut12")]
             {
                 let keys = self
                     .localstore
@@ -1003,7 +994,6 @@ impl Wallet {
 
             for proof in &mut proofs {
                 // Verify that proof DLEQ is valid
-                #[cfg(feature = "nut12")]
                 {
                     let keys = self.localstore.get_keys(&proof.keyset_id).await?.unwrap();
                     let key = keys.amount_key(proof.amount).unwrap();
@@ -1188,7 +1178,6 @@ impl Wallet {
     /// Verify all proofs in token have meet the required spend
     /// Can be used to allow a wallet to accept payments offline while reducing
     /// the risk of claiming back to the limits let by the spending_conditions
-    #[cfg(feature = "nut11")]
     pub fn verify_token_p2pk(
         &self,
         token: &Token,
@@ -1284,7 +1273,6 @@ impl Wallet {
     }
 
     /// Verify all proofs in token have a valid DLEQ proof
-    #[cfg(feature = "nut12")]
     pub async fn verify_token_dleq(&self, token: &Token) -> Result<(), Error> {
         let mut keys_cache: HashMap<Id, Keys> = HashMap::new();
 

+ 2 - 8
crates/cashu/Cargo.toml

@@ -15,14 +15,8 @@ description = "Cashu rust wallet and mint library"
 default = ["mint", "wallet", "all-nuts"]
 mint = []
 wallet = []
-all-nuts = ["nut07", "nut08", "nut09", "nut10", "nut11", "nut12", "nut13"]
-nut07 = []
-nut08 = []
-nut09 = []
-nut10 = []
-nut11 = ["nut10"]
-nut12 = []
-nut13 = ["dep:bip39", "nut09"]
+all-nuts = ["nut13"]
+nut13 = ["dep:bip39"]
 
 [dependencies]
 base64 = "0.21" # bitcoin uses v0.21 (optional dep)

+ 9 - 36
crates/cashu/src/dhke.rs

@@ -8,7 +8,6 @@ use bitcoin::secp256k1::{Parity, PublicKey as NormalizedPublicKey, Scalar, XOnly
 
 use crate::error::{self, Error};
 use crate::nuts::nut01::{PublicKey, SecretKey};
-#[cfg(feature = "nut12")]
 use crate::nuts::nut12::ProofDleq;
 use crate::nuts::{BlindSignature, Keys, Proof, Proofs};
 use crate::secret::Secret;
@@ -107,42 +106,16 @@ pub fn construct_proofs(
 
         let unblinded_signature: PublicKey = unblind_message(&blinded_c, &r, &a)?;
 
-        let proof;
-
-        #[cfg(not(feature = "nut12"))]
-        {
-            proof = Proof {
-                amount: blinded_signature.amount,
-                keyset_id: blinded_signature.keyset_id,
-                secret,
-                c: unblinded_signature,
-                #[cfg(feature = "nut11")]
-                witness: None,
-            };
-        }
+        let dleq = blinded_signature.dleq.map(|d| ProofDleq::new(d.e, d.s, r));
 
-        #[cfg(feature = "nut12")]
-        {
-            let dleq = if let Some(dleq) = blinded_signature.dleq {
-                Some(ProofDleq {
-                    e: dleq.e,
-                    s: dleq.s,
-                    r,
-                })
-            } else {
-                None
-            };
-
-            proof = Proof {
-                amount: blinded_signature.amount,
-                keyset_id: blinded_signature.keyset_id,
-                secret,
-                c: unblinded_signature,
-                #[cfg(feature = "nut11")]
-                witness: None,
-                dleq,
-            };
-        }
+        let proof = Proof {
+            amount: blinded_signature.amount,
+            keyset_id: blinded_signature.keyset_id,
+            secret,
+            c: unblinded_signature,
+            witness: None,
+            dleq,
+        };
 
         proofs.push(proof);
     }

+ 0 - 1
crates/cashu/src/lib.rs

@@ -5,7 +5,6 @@ pub use bitcoin::secp256k1;
 pub use lightning_invoice::{self, Bolt11Invoice};
 
 pub mod amount;
-#[cfg(any(feature = "wallet", feature = "mint"))]
 pub mod dhke;
 pub mod error;
 pub mod nuts;

+ 0 - 11
crates/cashu/src/nuts/mod.rs

@@ -5,17 +5,11 @@ pub mod nut03;
 pub mod nut04;
 pub mod nut05;
 pub mod nut06;
-#[cfg(feature = "nut07")]
 pub mod nut07;
-#[cfg(feature = "nut08")]
 pub mod nut08;
-#[cfg(feature = "nut09")]
 pub mod nut09;
-#[cfg(feature = "nut10")]
 pub mod nut10;
-#[cfg(feature = "nut11")]
 pub mod nut11;
-#[cfg(feature = "nut12")]
 pub mod nut12;
 #[cfg(feature = "nut13")]
 pub mod nut13;
@@ -37,15 +31,10 @@ pub use nut05::{
 };
 pub use nut06::{MintInfo, MintVersion, Nuts};
 #[cfg(feature = "wallet")]
-#[cfg(feature = "nut07")]
 pub use nut07::{CheckStateRequest, CheckStateResponse};
-#[cfg(feature = "nut09")]
 pub use nut09::{RestoreRequest, RestoreResponse};
-#[cfg(feature = "nut10")]
 pub use nut10::{Kind, Secret as Nut10Secret, SecretData};
-#[cfg(feature = "nut11")]
 pub use nut11::{P2PKConditions, SigFlag, Signatures, SigningKey, VerifyingKey};
-#[cfg(feature = "nut12")]
 pub use nut12::{BlindSignatureDleq, ProofDleq};
 
 pub type Proofs = Vec<Proof>;

+ 2 - 18
crates/cashu/src/nuts/nut00.rs

@@ -7,13 +7,8 @@ use std::str::FromStr;
 
 use serde::{Deserialize, Serialize};
 
-#[cfg(feature = "nut12")]
-use super::{BlindSignatureDleq, ProofDleq};
-use super::{Id, Proofs, PublicKey};
+use super::{BlindSignatureDleq, Id, ProofDleq, Proofs, PublicKey, Signatures};
 use crate::error::Error;
-#[cfg(feature = "nut11")]
-use crate::nuts::nut11::Signatures;
-#[cfg(feature = "nut11")]
 use crate::nuts::nut11::{witness_deserialize, witness_serialize};
 use crate::secret::Secret;
 use crate::url::UncheckedUrl;
@@ -31,7 +26,6 @@ pub struct BlindedMessage {
     #[serde(rename = "B_")]
     pub b: PublicKey,
     /// Witness
-    #[cfg(feature = "nut11")]
     #[serde(default)]
     #[serde(skip_serializing_if = "Option::is_none")]
     //#[serde(serialize_with = "witness_serialize")]
@@ -45,7 +39,6 @@ impl BlindedMessage {
             amount,
             keyset_id,
             b,
-            #[cfg(feature = "nut11")]
             witness: None,
         }
     }
@@ -124,9 +117,7 @@ pub mod wallet {
     use super::{CurrencyUnit, MintProofs};
     use crate::dhke::blind_message;
     use crate::error::wallet;
-    #[cfg(feature = "nut11")]
-    use crate::nuts::P2PKConditions;
-    use crate::nuts::{BlindedMessage, Id, Proofs, SecretKey};
+    use crate::nuts::{BlindedMessage, Id, P2PKConditions, Proofs, SecretKey};
     use crate::secret::Secret;
     use crate::url::UncheckedUrl;
     use crate::{error, Amount};
@@ -230,7 +221,6 @@ pub mod wallet {
             Ok(PreMintSecrets { secrets: output })
         }
 
-        #[cfg(feature = "nut11")]
         pub fn with_p2pk_conditions(
             keyset_id: Id,
             amount: Amount,
@@ -423,7 +413,6 @@ pub struct BlindSignature {
     #[serde(rename = "C_")]
     pub c: PublicKey,
     /// DLEQ Proof
-    #[cfg(feature = "nut12")]
     pub dleq: Option<BlindSignatureDleq>,
 }
 
@@ -440,16 +429,13 @@ pub struct Proof {
     /// Unblinded signature
     #[serde(rename = "C")]
     pub c: PublicKey,
-    #[cfg(feature = "nut11")]
     /// Witness
-    #[cfg(feature = "nut11")]
     #[serde(default)]
     #[serde(skip_serializing_if = "Option::is_none")]
     #[serde(serialize_with = "witness_serialize")]
     #[serde(deserialize_with = "witness_deserialize")]
     pub witness: Option<Signatures>,
     /// DLEQ Proof
-    #[cfg(feature = "nut12")]
     pub dleq: Option<ProofDleq>,
 }
 
@@ -460,9 +446,7 @@ impl Proof {
             keyset_id,
             secret,
             c,
-            #[cfg(feature = "nut11")]
             witness: None,
-            #[cfg(feature = "nut12")]
             dleq: None,
         }
     }

+ 2 - 4
crates/cashu/src/nuts/nut01/mod.rs

@@ -26,10 +26,8 @@ pub enum Error {
     InvalidPublicKeySize { expected: usize, found: usize },
 }
 
-/// Mint Keys
-///
-/// <https://github.com/cashubtc/nuts/blob/main/01.md>
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
+/// Mint Keys [NUT-01]
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
 pub struct Keys(BTreeMap<Amount, PublicKey>);
 
 impl From<mint::Keys> for Keys {

+ 1 - 5
crates/cashu/src/nuts/nut05.rs

@@ -4,9 +4,7 @@
 
 use serde::{Deserialize, Serialize};
 
-#[cfg(feature = "nut08")]
-use super::{BlindSignature, BlindedMessage};
-use super::{CurrencyUnit, PaymentMethod};
+use super::{BlindSignature, BlindedMessage, CurrencyUnit, PaymentMethod};
 use crate::nuts::Proofs;
 use crate::types::MeltQuote;
 use crate::{Amount, Bolt11Invoice};
@@ -56,7 +54,6 @@ pub struct MeltBolt11Request {
     pub inputs: Proofs,
     /// Blinded Message that can be used to return change [NUT-08]
     /// Amount field of BlindedMessages `SHOULD` be set to zero
-    #[cfg(feature = "nut08")]
     pub outputs: Option<Vec<BlindedMessage>>,
 }
 
@@ -74,7 +71,6 @@ pub struct MeltBolt11Response {
     /// Bolt11 preimage
     pub payment_preimage: Option<String>,
     /// Change
-    #[cfg(feature = "nut08")]
     pub change: Option<Vec<BlindSignature>>,
 }
 

+ 0 - 1
crates/cashu/src/nuts/nut11.rs

@@ -733,7 +733,6 @@ mod tests {
             )
             .unwrap(),
             witness: Some(Signatures { signatures: vec![] }),
-            #[cfg(feature = "nut12")]
             dleq: None,
         };
 

+ 6 - 0
crates/cashu/src/nuts/nut12.rs

@@ -41,6 +41,12 @@ pub struct ProofDleq {
     pub r: SecretKey,
 }
 
+impl ProofDleq {
+    pub fn new(e: SecretKey, s: SecretKey, r: SecretKey) -> Self {
+        Self { e, s, r }
+    }
+}
+
 /// Verify DLEQ
 fn verify_dleq(
     blinded_message: PublicKey,   // B'

+ 0 - 3
crates/cashu/src/secret.rs

@@ -61,7 +61,6 @@ impl Secret {
         self.as_bytes().to_vec()
     }
 
-    #[cfg(feature = "nut11")]
     pub fn is_p2pk(&self) -> bool {
         use crate::nuts::Kind;
 
@@ -104,7 +103,6 @@ impl From<&Secret> for Vec<u8> {
     }
 }
 
-#[cfg(feature = "nut10")]
 impl TryFrom<Secret> for crate::nuts::nut10::Secret {
     type Error = serde_json::Error;
 
@@ -113,7 +111,6 @@ impl TryFrom<Secret> for crate::nuts::nut10::Secret {
     }
 }
 
-#[cfg(feature = "nut10")]
 impl TryFrom<&Secret> for crate::nuts::nut10::Secret {
     type Error = serde_json::Error;