diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6a2ea79..fa7c66e5 100644 --- a/.github/workflows/ci.yml +++ b/.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 diff --git a/crates/cashu-sdk/Cargo.toml b/crates/cashu-sdk/Cargo.toml index 95757549..ed63984a 100644 --- a/crates/cashu-sdk/Cargo.toml +++ b/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"] diff --git a/crates/cashu-sdk/src/client/minreq_client.rs b/crates/cashu-sdk/src/client/minreq_client.rs index e11a38b0..78c40809 100644 --- a/crates/cashu-sdk/src/client/minreq_client.rs +++ b/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 { + let url = join_url(mint_url, &["v1", "info"])?; + + let res = minreq::get(url).send()?.json::()?; + + let response: Result = 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 { - let url = join_url(mint_url, &["v1", "info"])?; - - let res = minreq::get(url).send()?.json::()?; - - let response: Result = 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, diff --git a/crates/cashu-sdk/src/client/mod.rs b/crates/cashu-sdk/src/client/mod.rs index 757ca2d6..7445e4e9 100644 --- a/crates/cashu-sdk/src/client/mod.rs +++ b/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; - #[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; - #[cfg(feature = "nut09")] async fn post_restore( &self, mint_url: Url, diff --git a/crates/cashu-sdk/src/mint/mod.rs b/crates/cashu-sdk/src/mint/mod.rs index 54b2083f..b96a11a6 100644 --- a/crates/cashu-sdk/src/mint/mod.rs +++ b/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 { let output_len = request.outputs.len(); diff --git a/crates/cashu-sdk/src/wallet/mod.rs b/crates/cashu-sdk/src/wallet/mod.rs index c8f806dd..9d4d0431 100644 --- a/crates/cashu-sdk/src/wallet/mod.rs +++ b/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 = HashMap::new(); diff --git a/crates/cashu/Cargo.toml b/crates/cashu/Cargo.toml index b8f4e9bf..c92cf2bc 100644 --- a/crates/cashu/Cargo.toml +++ b/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) diff --git a/crates/cashu/src/dhke.rs b/crates/cashu/src/dhke.rs index 4bbcd90c..1e297ac3 100644 --- a/crates/cashu/src/dhke.rs +++ b/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; + let dleq = blinded_signature.dleq.map(|d| ProofDleq::new(d.e, d.s, r)); - #[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, - }; - } - - #[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); } diff --git a/crates/cashu/src/lib.rs b/crates/cashu/src/lib.rs index 1dc1eb9e..6e55d7ff 100644 --- a/crates/cashu/src/lib.rs +++ b/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; diff --git a/crates/cashu/src/nuts/mod.rs b/crates/cashu/src/nuts/mod.rs index 08f1b5a4..8e510787 100644 --- a/crates/cashu/src/nuts/mod.rs +++ b/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; diff --git a/crates/cashu/src/nuts/nut00.rs b/crates/cashu/src/nuts/nut00.rs index 4bcb2b4e..7fbc3f14 100644 --- a/crates/cashu/src/nuts/nut00.rs +++ b/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, } @@ -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, /// DLEQ Proof - #[cfg(feature = "nut12")] pub dleq: Option, } @@ -460,9 +446,7 @@ impl Proof { keyset_id, secret, c, - #[cfg(feature = "nut11")] witness: None, - #[cfg(feature = "nut12")] dleq: None, } } diff --git a/crates/cashu/src/nuts/nut01/mod.rs b/crates/cashu/src/nuts/nut01/mod.rs index 19312b23..3339d5b6 100644 --- a/crates/cashu/src/nuts/nut01/mod.rs +++ b/crates/cashu/src/nuts/nut01/mod.rs @@ -26,10 +26,8 @@ pub enum Error { InvalidPublicKeySize { expected: usize, found: usize }, } -/// Mint Keys -/// -/// -#[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); impl From for Keys { diff --git a/crates/cashu/src/nuts/nut05.rs b/crates/cashu/src/nuts/nut05.rs index bc245f07..6826d412 100644 --- a/crates/cashu/src/nuts/nut05.rs +++ b/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>, } @@ -74,7 +71,6 @@ pub struct MeltBolt11Response { /// Bolt11 preimage pub payment_preimage: Option, /// Change - #[cfg(feature = "nut08")] pub change: Option>, } diff --git a/crates/cashu/src/nuts/nut11.rs b/crates/cashu/src/nuts/nut11.rs index a5a7f6b3..00ab6784 100644 --- a/crates/cashu/src/nuts/nut11.rs +++ b/crates/cashu/src/nuts/nut11.rs @@ -733,7 +733,6 @@ mod tests { ) .unwrap(), witness: Some(Signatures { signatures: vec![] }), - #[cfg(feature = "nut12")] dleq: None, }; diff --git a/crates/cashu/src/nuts/nut12.rs b/crates/cashu/src/nuts/nut12.rs index 30373dd5..aeae791d 100644 --- a/crates/cashu/src/nuts/nut12.rs +++ b/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' diff --git a/crates/cashu/src/secret.rs b/crates/cashu/src/secret.rs index a420a2db..451a5edb 100644 --- a/crates/cashu/src/secret.rs +++ b/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 { } } -#[cfg(feature = "nut10")] impl TryFrom for crate::nuts::nut10::Secret { type Error = serde_json::Error; @@ -113,7 +111,6 @@ impl TryFrom for crate::nuts::nut10::Secret { } } -#[cfg(feature = "nut10")] impl TryFrom<&Secret> for crate::nuts::nut10::Secret { type Error = serde_json::Error;