refactor: reduce number of features

Features for NUTs without extra deps expand the
feature matrix without much benefit.
This commit is contained in:
thesimplekid
2024-04-09 21:38:37 +01:00
parent 4c2cb78327
commit e5a531dca1
16 changed files with 58 additions and 208 deletions

View File

@@ -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

View File

@@ -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"]

View File

@@ -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,

View File

@@ -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,

View File

@@ -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();

View File

@@ -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();

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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>;

View File

@@ -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,
}
}

View File

@@ -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 {

View File

@@ -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>>,
}

View File

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

View File

@@ -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'

View File

@@ -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;