diff --git a/crates/cdk/src/dhke.rs b/crates/cdk/src/dhke.rs index 1e297ac3..1bacb251 100644 --- a/crates/cdk/src/dhke.rs +++ b/crates/cdk/src/dhke.rs @@ -6,7 +6,7 @@ use bitcoin::hashes::sha256::Hash as Sha256Hash; use bitcoin::hashes::Hash; use bitcoin::secp256k1::{Parity, PublicKey as NormalizedPublicKey, Scalar, XOnlyPublicKey}; -use crate::error::{self, Error}; +use crate::error::Error; use crate::nuts::nut01::{PublicKey, SecretKey}; use crate::nuts::nut12::ProofDleq; use crate::nuts::{BlindSignature, Keys, Proof, Proofs}; @@ -62,7 +62,7 @@ where pub fn blind_message( secret: &[u8], blinding_factor: Option, -) -> Result<(PublicKey, SecretKey), error::wallet::Error> { +) -> Result<(PublicKey, SecretKey), Error> { let y: PublicKey = hash_to_curve(secret)?; let r: SecretKey = blinding_factor.unwrap_or_else(SecretKey::generate); Ok((y.combine(&r.public_key())?.into(), r)) @@ -77,7 +77,7 @@ pub fn unblind_message( r: &SecretKey, // K mint_pubkey: &PublicKey, -) -> Result { +) -> Result { let r: Scalar = Scalar::from(r.deref().to_owned()); // a = r * K @@ -94,15 +94,13 @@ pub fn construct_proofs( rs: Vec, secrets: Vec, keys: &Keys, -) -> Result { +) -> Result { let mut proofs = vec![]; for ((blinded_signature, r), secret) in promises.into_iter().zip(rs).zip(secrets) { let blinded_c: PublicKey = blinded_signature.c; - let a: PublicKey = - keys.amount_key(blinded_signature.amount) - .ok_or(error::wallet::Error::CustomError( - "Could not get proofs".to_string(), - ))?; + let a: PublicKey = keys + .amount_key(blinded_signature.amount) + .ok_or(Error::CustomError("Could not get proofs".to_string()))?; let unblinded_signature: PublicKey = unblind_message(&blinded_c, &r, &a)?; @@ -129,10 +127,7 @@ pub fn construct_proofs( /// * `k` is the private key of mint (one for each amount) /// * `B_` is the blinded message #[inline] -pub fn sign_message( - k: &SecretKey, - blinded_message: &PublicKey, -) -> Result { +pub fn sign_message(k: &SecretKey, blinded_message: &PublicKey) -> Result { let k: Scalar = Scalar::from(k.deref().to_owned()); Ok(blinded_message.mul_tweak(&SECP256K1, &k)?.into()) } @@ -142,7 +137,7 @@ pub fn verify_message( a: &SecretKey, unblinded_message: PublicKey, msg: &[u8], -) -> Result<(), error::mint::Error> { +) -> Result<(), Error> { // Y let y: PublicKey = hash_to_curve(msg)?; @@ -154,7 +149,7 @@ pub fn verify_message( return Ok(()); } - Err(error::mint::Error::TokenNotVerifed) + Err(Error::TokenNotVerifed) } #[cfg(test)] diff --git a/crates/cdk/src/error/mint.rs b/crates/cdk/src/error/mint.rs deleted file mode 100644 index 34311099..00000000 --- a/crates/cdk/src/error/mint.rs +++ /dev/null @@ -1,38 +0,0 @@ -use thiserror::Error; - -use crate::nuts::nut01; - -#[derive(Debug, Error)] -pub enum Error { - #[error("No key for amount")] - AmountKey, - #[error("Amount miss match")] - Amount, - #[error("Token Already Spent")] - TokenSpent, - /// Secp256k1 error - #[error(transparent)] - Secp256k1(#[from] bitcoin::secp256k1::Error), - /// NUT01 error - #[error(transparent)] - NUT01(#[from] nut01::Error), - #[error("`Token not verified`")] - TokenNotVerifed, - #[error("Invoice amount undefined")] - InvoiceAmountUndefined, - /// Duplicate Proofs sent in request - #[error("Duplicate proofs")] - DuplicateProofs, - /// Keyset id not active - #[error("Keyset id is not active")] - InactiveKeyset, - /// Keyset is not known - #[error("Unknown Keyset")] - UnknownKeySet, - #[error(transparent)] - Secret(#[from] crate::secret::Error), - #[error(transparent)] - Cashu(#[from] super::Error), - #[error("`{0}`")] - CustomError(String), -} diff --git a/crates/cdk/src/error/mod.rs b/crates/cdk/src/error/mod.rs index 45aa290d..4c34b81e 100644 --- a/crates/cdk/src/error/mod.rs +++ b/crates/cdk/src/error/mod.rs @@ -1,3 +1,5 @@ +//! Errors + use std::string::FromUtf8Error; use serde::{Deserialize, Serialize}; @@ -5,68 +7,66 @@ use thiserror::Error; use crate::util::hex; -pub mod mint; -pub mod wallet; - #[derive(Debug, Error)] pub enum Error { - /// Parse Url Error - #[error(transparent)] - UrlParseError(#[from] url::ParseError), - /// Utf8 parse error - #[error(transparent)] - Utf8ParseError(#[from] FromUtf8Error), - /// Serde Json error - #[error(transparent)] - SerdeJsonError(#[from] serde_json::Error), - /// Base64 error - #[error(transparent)] - Base64Error(#[from] base64::DecodeError), - /// From hex error - #[error(transparent)] - HexError(#[from] hex::Error), - /// Secp256k1 error - #[error(transparent)] - Secp256k1(#[from] bitcoin::secp256k1::Error), - #[error("No Key for Amoun")] + /// Mint does not have a key for amount + #[error("No Key for Amount")] AmountKey, + /// Amount is not what expected #[error("Amount miss match")] Amount, + /// Token is already spent #[error("Token already spent")] TokenSpent, + /// Token could not be validated #[error("Token not verified")] TokenNotVerifed, + /// Bolt11 invoice does not have amount #[error("Invoice Amount undefined")] InvoiceAmountUndefined, + /// Proof is missing a required field #[error("Proof missing required field")] MissingProofField, + /// No valid point on curve #[error("No valid point found")] NoValidPoint, - #[error("Kind not found")] - KindNotFound, - #[error("Unknown Tag")] - UnknownTag, - #[error("Incorrect Secret Kind")] - IncorrectSecretKind, - #[error("Spending conditions not met")] - SpendConditionsNotMet, - #[error("Could not convert key")] - Key, - #[error("Invalid signature")] - InvalidSignature, - #[error("Locktime in past")] - LocktimeInPast, - #[error(transparent)] - Secret(#[from] super::secret::Error), + /// Secp256k1 error #[error(transparent)] - NUT01(#[from] crate::nuts::nut01::Error), + Secp256k1(#[from] bitcoin::secp256k1::Error), + /// Secret error #[error(transparent)] - NUT02(#[from] crate::nuts::nut02::Error), + Secret(#[from] super::secret::Error), + /// Bip32 error #[cfg(feature = "nut13")] #[error(transparent)] Bip32(#[from] bitcoin::bip32::Error), + /// Parse int error #[error(transparent)] ParseInt(#[from] std::num::ParseIntError), + /// Parse Url Error + #[error(transparent)] + UrlParseError(#[from] url::ParseError), + /// Utf8 parse error + #[error(transparent)] + Utf8ParseError(#[from] FromUtf8Error), + /// Serde Json error + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), + /// Base64 error + #[error(transparent)] + Base64Error(#[from] base64::DecodeError), + /// From hex error + #[error(transparent)] + HexError(#[from] hex::Error), + /// Nut01 error + #[error(transparent)] + NUT01(#[from] crate::nuts::nut01::Error), + /// NUT02 error + #[error(transparent)] + NUT02(#[from] crate::nuts::nut02::Error), + /// NUT11 Error + #[error(transparent)] + NUT11(#[from] crate::nuts::nut11::Error), /// Custom error #[error("`{0}`")] CustomError(String), diff --git a/crates/cdk/src/error/wallet.rs b/crates/cdk/src/error/wallet.rs deleted file mode 100644 index 356416f9..00000000 --- a/crates/cdk/src/error/wallet.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::string::FromUtf8Error; - -use thiserror::Error; - -use crate::nuts::nut01; - -#[derive(Debug, Error)] -pub enum Error { - /// Serde Json error - #[error(transparent)] - SerdeJsonError(#[from] serde_json::Error), - /// Secp256k1 error - #[error(transparent)] - Secp256k1(#[from] bitcoin::secp256k1::Error), - /// NUT01 error - #[error(transparent)] - NUT01(#[from] nut01::Error), - /// Insufficient Funds - #[error("Insufficient funds")] - InsufficientFunds, - /// Utf8 parse error - #[error(transparent)] - Utf8ParseError(#[from] FromUtf8Error), - /// Base64 error - #[error(transparent)] - Base64Error(#[from] base64::DecodeError), - /// Unsupported Token - #[error("Token unsupported")] - UnsupportedToken, - /// Token Requires proofs - #[error("Proofs Required")] - ProofsRequired, - /// Url Parse error - #[error("Url Parse")] - UrlParse, - #[error(transparent)] - Secret(#[from] crate::secret::Error), - #[error(transparent)] - Cashu(#[from] super::Error), - /// Custom Error message - #[error("`{0}`")] - CustomError(String), -} - -impl From for Error { - fn from(_err: crate::url::Error) -> Error { - Error::UrlParse - } -} diff --git a/crates/cdk/src/mint/localstore/mod.rs b/crates/cdk/src/mint/localstore/mod.rs index c57701d9..4c323960 100644 --- a/crates/cdk/src/mint/localstore/mod.rs +++ b/crates/cdk/src/mint/localstore/mod.rs @@ -46,6 +46,8 @@ pub enum Error { #[error(transparent)] Cashu(#[from] crate::error::Error), #[error(transparent)] + NUT00(#[from] crate::nuts::nut00::Error), + #[error(transparent)] CashuNut02(#[from] crate::nuts::nut02::Error), #[error(transparent)] Secret(#[from] crate::secret::Error), diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index 68af9ae7..a950c7fe 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -9,15 +9,11 @@ use tracing::{debug, error, info}; use crate::dhke::{hash_to_curve, sign_message, verify_message}; use crate::error::ErrorResponse; -use crate::nuts::nut07::{ProofState, State}; -use crate::nuts::{ - BlindSignature, BlindedMessage, CheckStateRequest, CheckStateResponse, MeltBolt11Request, - MeltBolt11Response, Proof, RestoreRequest, RestoreResponse, SwapRequest, SwapResponse, *, -}; +use crate::nuts::*; use crate::types::{MeltQuote, MintQuote}; use crate::Amount; -mod localstore; +pub mod localstore; #[cfg(all(not(target_arch = "wasm32"), feature = "redb"))] pub use localstore::RedbLocalStore; pub use localstore::{LocalStore, MemoryLocalStore}; @@ -45,14 +41,16 @@ pub enum Error { #[error("`{0}`")] Custom(String), #[error(transparent)] - CashuMint(#[from] crate::error::mint::Error), - #[error(transparent)] Cashu(#[from] crate::error::Error), #[error(transparent)] Localstore(#[from] localstore::Error), #[error(transparent)] Secret(#[from] crate::secret::Error), #[error(transparent)] + NUT00(#[from] crate::nuts::nut00::Error), + #[error(transparent)] + NUT11(#[from] crate::nuts::nut11::Error), + #[error(transparent)] Nut12(#[from] crate::nuts::nut12::Error), #[error("Unknown quote")] UnknownQuote, diff --git a/crates/cdk/src/nuts/nut00.rs b/crates/cdk/src/nuts/nut00.rs index 7fbc3f14..5a73c095 100644 --- a/crates/cdk/src/nuts/nut00.rs +++ b/crates/cdk/src/nuts/nut00.rs @@ -4,16 +4,48 @@ use std::fmt; use std::hash::{self, Hasher}; use std::str::FromStr; +use std::string::FromUtf8Error; use serde::{Deserialize, Serialize}; +use thiserror::Error; use super::{BlindSignatureDleq, Id, ProofDleq, Proofs, PublicKey, Signatures}; -use crate::error::Error; use crate::nuts::nut11::{witness_deserialize, witness_serialize}; use crate::secret::Secret; use crate::url::UncheckedUrl; use crate::Amount; +#[derive(Debug, Error)] +pub enum Error { + /// Proofs required + #[error("Proofs required in token")] + ProofsRequired, + /// Unsupported token + #[error("Unsupported token")] + UnsupportedToken, + /// Invalid Url + #[error("Invalid Url")] + InvalidUrl, + /// Serde Json error + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), + /// Utf8 parse error + #[error(transparent)] + Utf8ParseError(#[from] FromUtf8Error), + /// Base64 error + #[error(transparent)] + Base64Error(#[from] base64::DecodeError), + /// Parse Url Error + #[error(transparent)] + UrlParseError(#[from] url::ParseError), + /// CDK error + #[error(transparent)] + Cdk(#[from] crate::error::Error), + /// NUT11 error + #[error(transparent)] + NUT11(#[from] crate::nuts::nut11::Error), +} + /// Blinded Message [NUT-00] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct BlindedMessage { @@ -114,13 +146,12 @@ pub mod wallet { use serde::{Deserialize, Serialize}; use url::Url; - use super::{CurrencyUnit, MintProofs}; + use super::{CurrencyUnit, MintProofs, *}; use crate::dhke::blind_message; - use crate::error::wallet; use crate::nuts::{BlindedMessage, Id, P2PKConditions, Proofs, SecretKey}; use crate::secret::Secret; use crate::url::UncheckedUrl; - use crate::{error, Amount}; + use crate::Amount; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct PreMint { @@ -153,7 +184,7 @@ pub mod wallet { impl PreMintSecrets { /// Outputs for speceifed amount with random secret - pub fn random(keyset_id: Id, amount: Amount) -> Result { + pub fn random(keyset_id: Id, amount: Amount) -> Result { let amount_split = amount.split(); let mut output = Vec::with_capacity(amount_split.len()); @@ -330,13 +361,13 @@ pub mod wallet { proofs: Proofs, memo: Option, unit: Option, - ) -> Result { + ) -> Result { if proofs.is_empty() { - return Err(wallet::Error::ProofsRequired); + return Err(Error::ProofsRequired); } // Check Url is valid - let _: Url = (&mint_url).try_into()?; + let _: Url = (&mint_url).try_into().map_err(|_| Error::InvalidUrl)?; Ok(Self { token: vec![MintProofs::new(mint_url, proofs)], @@ -359,13 +390,13 @@ pub mod wallet { } impl FromStr for Token { - type Err = error::wallet::Error; + type Err = Error; fn from_str(s: &str) -> Result { let s = if s.starts_with("cashuA") { s.replace("cashuA", "") } else { - return Err(wallet::Error::UnsupportedToken); + return Err(Error::UnsupportedToken); }; let decode_config = general_purpose::GeneralPurposeConfig::new() diff --git a/crates/cdk/src/nuts/nut11.rs b/crates/cdk/src/nuts/nut11.rs index 00ab6784..dfdbac78 100644 --- a/crates/cdk/src/nuts/nut11.rs +++ b/crates/cdk/src/nuts/nut11.rs @@ -16,15 +16,60 @@ use bitcoin::secp256k1::{ use serde::de::Error as DeserializerError; use serde::ser::SerializeSeq; use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer}; +use thiserror::Error; use super::nut01::PublicKey; use super::nut10::{Secret, SecretData}; use super::{Proof, SecretKey}; -use crate::error::Error; use crate::nuts::nut00::BlindedMessage; use crate::util::{hex, unix_time}; use crate::SECP256K1; +#[derive(Debug, Error)] +pub enum Error { + /// Incorrect secret kind + #[error("Secret is not a p2pk secret")] + IncorrectSecretKind, + /// P2PK locktime has already passed + #[error("Locktime in past")] + LocktimeInPast, + /// Witness signature is not valid + #[error("Invalid signature")] + InvalidSignature, + /// Unknown tag in P2PK secret + #[error("Unknown Tag P2PK secret")] + UnknownTag, + /// P2PK Spend conditions not meet + #[error("P2PK Spend conditions are not met")] + SpendConditionsNotMet, + /// Pubkey must be in data field of P2PK + #[error("P2PK Required in secret data")] + P2PKPubkeyRequired, + #[error("Kind not found")] + KindNotFound, + /// Parse Url Error + #[error(transparent)] + UrlParseError(#[from] url::ParseError), + /// Parse int error + #[error(transparent)] + ParseInt(#[from] std::num::ParseIntError), + /// From hex error + #[error(transparent)] + HexError(#[from] hex::Error), + /// Serde Json error + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), + /// Secp256k1 error + #[error(transparent)] + Secp256k1(#[from] bitcoin::secp256k1::Error), + /// NUT01 Error + #[error(transparent)] + NUT01(#[from] crate::nuts::nut01::Error), + /// Secret error + #[error(transparent)] + Secret(#[from] crate::secret::Error), +} + #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Signatures { #[serde(default)] @@ -219,12 +264,10 @@ impl TryFrom for Secret { sig_flag, } = conditions; - // Check there is at least one pubkey - if pubkeys.len().lt(&1) { - return Err(Error::Amount); - } - - let data: PublicKey = pubkeys[0].clone().to_normalized_public_key(); + let data = match pubkeys.first() { + Some(data) => data.to_string(), + None => return Err(Error::P2PKPubkeyRequired), + }; let data = data.to_string(); @@ -263,7 +306,7 @@ impl TryFrom for crate::secret::Secret { fn try_from(conditions: P2PKConditions) -> Result { let secret: Secret = conditions.try_into()?; - secret.try_into() + secret.try_into().map_err(|_| Error::IncorrectSecretKind) } } @@ -597,7 +640,7 @@ impl TryFrom<&PublicKey> for VerifyingKey { bytes.to_vec() }; - VerifyingKey::from_bytes(&bytes).map_err(|_| Error::Key) + VerifyingKey::from_bytes(&bytes) } } diff --git a/crates/cdk/src/nuts/nut13.rs b/crates/cdk/src/nuts/nut13.rs index a9a25d79..dc753748 100644 --- a/crates/cdk/src/nuts/nut13.rs +++ b/crates/cdk/src/nuts/nut13.rs @@ -63,7 +63,7 @@ mod wallet { use bip39::Mnemonic; use crate::dhke::blind_message; - use crate::error::wallet; + use crate::error::Error; use crate::nuts::{BlindedMessage, Id, PreMint, PreMintSecrets, SecretKey}; use crate::secret::Secret; use crate::Amount; @@ -77,7 +77,7 @@ mod wallet { mnemonic: &Mnemonic, amount: Amount, zero_amount: bool, - ) -> Result { + ) -> Result { let mut pre_mint_secrets = PreMintSecrets::default(); let mut counter = counter; @@ -113,7 +113,7 @@ mod wallet { mnemonic: &Mnemonic, start_count: u64, end_count: u64, - ) -> Result { + ) -> Result { let mut pre_mint_secrets = PreMintSecrets::default(); for i in start_count..=end_count { diff --git a/crates/cdk/src/wallet/mod.rs b/crates/cdk/src/wallet/mod.rs index 310e8799..f286b3af 100644 --- a/crates/cdk/src/wallet/mod.rs +++ b/crates/cdk/src/wallet/mod.rs @@ -28,22 +28,15 @@ pub enum Error { /// Insufficient Funds #[error("Insufficient Funds")] InsufficientFunds, - #[error("`{0}`")] - CashuWallet(#[from] crate::error::wallet::Error), - #[error("`{0}`")] - Client(#[from] crate::client::Error), - /// Cashu Url Error - #[error("`{0}`")] - CashuUrl(#[from] crate::url::Error), #[error("Quote Expired")] QuoteExpired, #[error("Quote Unknown")] QuoteUnknown, #[error("No active keyset")] NoActiveKeyset, - #[error("`{0}`")] + #[error(transparent)] LocalStore(#[from] localstore::Error), - #[error("`{0}`")] + #[error(transparent)] Cashu(#[from] crate::error::Error), #[error("Could not verify Dleq")] CouldNotVerifyDleq, @@ -53,8 +46,19 @@ pub enum Error { InvalidSpendConditions(String), #[error("Unknown Key")] UnknownKey, - #[error("`{0}`")] + #[error(transparent)] ParseInt(#[from] ParseIntError), + #[error(transparent)] + Client(#[from] crate::client::Error), + /// Cashu Url Error + #[error(transparent)] + CashuUrl(#[from] crate::url::Error), + /// NUT00 Error + #[error(transparent)] + NUT00(#[from] crate::nuts::nut00::Error), + /// NUT11 Error + #[error(transparent)] + NUT11(#[from] crate::nuts::nut11::Error), #[error("`{0}`")] Custom(String), }