refactor: move client to wallet mod

refactor: client error

refactor: mint error mod

refactor: error codes

refactor: remove enum numbers
This commit is contained in:
thesimplekid
2024-06-01 15:28:04 +01:00
parent 6103bb1d87
commit 1ab1f47885
7 changed files with 286 additions and 185 deletions

View File

@@ -3,7 +3,7 @@
use std::fmt;
use std::string::FromUtf8Error;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use thiserror::Error;
@@ -79,7 +79,7 @@ pub enum Error {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ErrorResponse {
pub code: u32,
pub code: ErrorCode,
pub error: Option<String>,
pub detail: Option<String>,
}
@@ -98,24 +98,73 @@ impl fmt::Display for ErrorResponse {
impl ErrorResponse {
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
match serde_json::from_str::<ErrorResponse>(json) {
Ok(res) => Ok(res),
Err(_) => Ok(Self {
code: 999,
error: Some(json.to_string()),
detail: None,
}),
}
let value: Value = serde_json::from_str(json)?;
Self::from_value(value)
}
pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
match serde_json::from_value::<ErrorResponse>(value.clone()) {
Ok(res) => Ok(res),
Err(_) => Ok(Self {
code: 999,
code: ErrorCode::Unknown(999),
error: Some(value.to_string()),
detail: None,
}),
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum ErrorCode {
TokenAlreadySpent,
QuoteNotPaid,
KeysetNotFound,
Unknown(u16),
}
impl Serialize for ErrorCode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let code = match self {
ErrorCode::TokenAlreadySpent => 11001,
ErrorCode::QuoteNotPaid => 20001,
ErrorCode::KeysetNotFound => 12001,
ErrorCode::Unknown(code) => *code,
};
serializer.serialize_u16(code)
}
}
impl<'de> Deserialize<'de> for ErrorCode {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let code = u16::deserialize(deserializer)?;
let error_code = match code {
11001 => ErrorCode::TokenAlreadySpent,
20001 => ErrorCode::QuoteNotPaid,
12001 => ErrorCode::KeysetNotFound,
c => ErrorCode::Unknown(c),
};
Ok(error_code)
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let code = match self {
Self::TokenAlreadySpent => 11001,
Self::QuoteNotPaid => 20001,
Self::KeysetNotFound => 12001,
Self::Unknown(code) => *code,
};
write!(f, "{}", code)
}
}

View File

@@ -8,8 +8,6 @@ pub use lightning_invoice::{self, Bolt11Invoice};
pub mod amount;
pub mod cdk_database;
#[cfg(feature = "wallet")]
pub mod client;
pub mod dhke;
pub mod error;
#[cfg(feature = "mint")]
@@ -23,8 +21,8 @@ pub mod util;
pub mod wallet;
pub use self::amount::Amount;
#[cfg(feature = "wallet")]
pub use self::client::HttpClient;
pub use self::util::SECP256K1;
#[cfg(feature = "wallet")]
pub use self::wallet::client::HttpClient;
pub type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;

View File

@@ -0,0 +1,100 @@
use http::StatusCode;
use thiserror::Error;
use crate::cdk_database;
use crate::error::{ErrorCode, ErrorResponse};
#[derive(Debug, Error)]
pub enum Error {
/// Unknown Keyset
#[error("Unknown Keyset")]
UnknownKeySet,
/// Inactive Keyset
#[error("Inactive Keyset")]
InactiveKeyset,
#[error("No key for amount")]
AmountKey,
#[error("Amount")]
Amount,
#[error("Duplicate proofs")]
DuplicateProofs,
#[error("Token Already Spent")]
TokenAlreadySpent,
#[error("Token Pending")]
TokenPending,
#[error("Quote not paid")]
UnpaidQuote,
#[error("Unknown quote")]
UnknownQuote,
#[error("Unknown secret kind")]
UnknownSecretKind,
#[error("Cannot have multiple units")]
MultipleUnits,
#[error("Blinded Message is already signed")]
BlindedMessageAlreadySigned,
#[error(transparent)]
Cashu(#[from] crate::error::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(transparent)]
Nut14(#[from] crate::nuts::nut14::Error),
/// Database Error
#[error(transparent)]
Database(#[from] cdk_database::Error),
#[error("`{0}`")]
Custom(String),
}
impl From<Error> for cdk_database::Error {
fn from(e: Error) -> Self {
Self::Database(Box::new(e))
}
}
impl From<Error> for ErrorResponse {
fn from(err: Error) -> ErrorResponse {
match err {
Error::TokenAlreadySpent => ErrorResponse {
code: ErrorCode::TokenAlreadySpent,
error: Some(err.to_string()),
detail: None,
},
_ => ErrorResponse {
code: ErrorCode::Unknown(9999),
error: Some(err.to_string()),
detail: None,
},
}
}
}
impl From<Error> for (StatusCode, ErrorResponse) {
fn from(err: Error) -> (StatusCode, ErrorResponse) {
(StatusCode::NOT_FOUND, err.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_response_enum() {
let error = Error::TokenAlreadySpent;
let response: ErrorResponse = error.into();
let json = serde_json::to_string(&response).unwrap();
let error_response: ErrorResponse = serde_json::from_str(&json).unwrap();
assert_eq!(response.code, error_response.code);
}
}

View File

@@ -3,90 +3,21 @@ use std::sync::Arc;
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
use bitcoin::secp256k1::{self, Secp256k1};
use http::StatusCode;
use error::Error;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tokio::sync::RwLock;
use tracing::{debug, error, info};
use self::nut11::enforce_sig_flag;
use crate::cdk_database::{self, MintDatabase};
use crate::dhke::{hash_to_curve, sign_message, verify_message};
use crate::error::ErrorResponse;
use crate::nuts::nut11::enforce_sig_flag;
use crate::nuts::*;
use crate::types::{MeltQuote, MintQuote};
use crate::url::UncheckedUrl;
use crate::util::unix_time;
use crate::Amount;
#[derive(Debug, Error)]
pub enum Error {
/// Unknown Keyset
#[error("Unknown Keyset")]
UnknownKeySet,
/// Inactive Keyset
#[error("Inactive Keyset")]
InactiveKeyset,
#[error("No key for amount")]
AmountKey,
#[error("Amount")]
Amount,
#[error("Duplicate proofs")]
DuplicateProofs,
#[error("Token Spent")]
TokenSpent,
#[error("Token Pending")]
TokenPending,
#[error("Quote not paid")]
UnpaidQuote,
#[error("`{0}`")]
Custom(String),
#[error(transparent)]
Cashu(#[from] crate::error::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(transparent)]
Nut14(#[from] crate::nuts::nut14::Error),
/// Database Error
#[error(transparent)]
Database(#[from] crate::cdk_database::Error),
#[error("Unknown quote")]
UnknownQuote,
#[error("Unknown secret kind")]
UnknownSecretKind,
#[error("Cannot have multiple units")]
MultipleUnits,
#[error("Blinded Message is already signed")]
BlindedMessageAlreadySigned,
}
impl From<Error> for cdk_database::Error {
fn from(e: Error) -> Self {
Self::Database(Box::new(e))
}
}
impl From<Error> for ErrorResponse {
fn from(err: Error) -> ErrorResponse {
ErrorResponse {
code: 9999,
error: Some(err.to_string()),
detail: None,
}
}
}
impl From<Error> for (StatusCode, ErrorResponse) {
fn from(err: Error) -> (StatusCode, ErrorResponse) {
(StatusCode::NOT_FOUND, err.into())
}
}
pub mod error;
#[derive(Clone)]
pub struct Mint {
@@ -493,7 +424,7 @@ impl Mint {
let y: PublicKey = hash_to_curve(&proof.secret.to_bytes())?;
if self.localstore.get_spent_proof_by_y(&y).await?.is_some() {
return Err(Error::TokenSpent);
return Err(Error::TokenAlreadySpent);
}
if self.localstore.get_pending_proof_by_y(&y).await?.is_some() {

View File

@@ -2,10 +2,10 @@
use reqwest::Client;
use serde_json::Value;
use thiserror::Error;
use tracing::instrument;
use url::Url;
use super::Error;
use crate::error::ErrorResponse;
use crate::nuts::{
BlindedMessage, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysResponse,
@@ -16,28 +16,6 @@ use crate::nuts::{
};
use crate::{Amount, Bolt11Invoice};
#[derive(Debug, Error)]
pub enum Error {
/// Unknown Keyset
#[error("Url Path segments could not be joined")]
UrlPathSegments,
/// Serde Json error
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
/// From hex error
#[error(transparent)]
ReqwestError(#[from] reqwest::Error),
/// Unknown error response
#[error("Unknown Error response: `{0}`")]
UnknownErrorResponse(String),
}
impl From<ErrorResponse> for Error {
fn from(err: ErrorResponse) -> Error {
Self::UnknownErrorResponse(err.to_string())
}
}
fn join_url(url: Url, paths: &[&str]) -> Result<Url, Error> {
let mut url = url;
for path in paths {

View File

@@ -0,0 +1,115 @@
use std::num::ParseIntError;
use thiserror::Error;
use crate::cdk_database;
use crate::error::{ErrorCode, ErrorResponse};
#[derive(Debug, Error)]
pub enum Error {
/// Insufficient Funds
#[error("Insufficient Funds")]
InsufficientFunds,
/// Quote Expired
#[error("Quote Expired")]
QuoteExpired,
/// Unknown Quote
#[error("Quote Unknown")]
QuoteUnknown,
/// Not active keyset
#[error("No active keyset")]
NoActiveKeyset,
/// Invalid DLEQ prood
#[error("Could not verify Dleq")]
CouldNotVerifyDleq,
/// P2PK spending conditions not met
#[error("P2PK Condition Not met `{0}`")]
P2PKConditionsNotMet(String),
/// Invalid Spending Conditions
#[error("Invalid Spending Conditions: `{0}`")]
InvalidSpendConditions(String),
/// Preimage not provided
#[error("Preimage not provided")]
PreimageNotProvided,
#[error("Unknown Key")]
UnknownKey,
/// Spending Locktime not provided
#[error("Spending condition locktime not provided")]
LocktimeNotProvided,
/// Unknown Keyset
#[error("Url Path segments could not be joined")]
UrlPathSegments,
/// Quote not paid
#[error("Quote not paid")]
QuoteNotePaid,
/// Token Already spent error
#[error("Token Already Spent Error")]
TokenAlreadySpent,
/// Keyset Not Found
#[error("Keyset Not Found")]
KeysetNotFound,
/// From hex error
#[error(transparent)]
ReqwestError(#[from] reqwest::Error),
/// Unknown error response
#[error("Unknown Error response: `{0}`")]
UnknownErrorResponse(String),
/// CDK Error
#[error(transparent)]
Cashu(#[from] crate::error::Error),
/// Cashu Url Error
#[error(transparent)]
CashuUrl(#[from] crate::url::Error),
/// Database Error
#[error(transparent)]
Database(#[from] crate::cdk_database::Error),
/// NUT00 Error
#[error(transparent)]
NUT00(#[from] crate::nuts::nut00::Error),
/// NUT01 Error
#[error(transparent)]
NUT01(#[from] crate::nuts::nut01::Error),
/// NUT11 Error
#[error(transparent)]
NUT11(#[from] crate::nuts::nut11::Error),
/// NUT12 Error
#[error(transparent)]
NUT12(#[from] crate::nuts::nut12::Error),
/// Parse int
#[error(transparent)]
ParseInt(#[from] ParseIntError),
/// Parse invoice error
#[error(transparent)]
Invoice(#[from] lightning_invoice::ParseOrSemanticError),
/// Serde Error
#[error(transparent)]
Serde(#[from] serde_json::Error),
/// Nostr Client Error
#[cfg(feature = "nostr")]
#[error(transparent)]
NostrClient(#[from] nostr_sdk::client::Error),
/// Nostr Key Error
#[cfg(feature = "nostr")]
#[error(transparent)]
NostrKey(#[from] nostr_sdk::key::Error),
/// Custom Error
#[error("`{0}`")]
Custom(String),
}
impl From<Error> for cdk_database::Error {
fn from(e: Error) -> Self {
Self::Database(Box::new(e))
}
}
impl From<ErrorResponse> for Error {
fn from(err: ErrorResponse) -> Error {
match err.code {
ErrorCode::QuoteNotPaid => Self::QuoteNotePaid,
ErrorCode::TokenAlreadySpent => Self::TokenAlreadySpent,
ErrorCode::KeysetNotFound => Self::KeysetNotFound,
_ => Self::UnknownErrorResponse(err.to_string()),
}
}
}

View File

@@ -1,7 +1,6 @@
//! Cashu Wallet
use std::collections::{HashMap, HashSet};
use std::num::ParseIntError;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
@@ -11,17 +10,16 @@ use bitcoin::hashes::sha256::Hash as Sha256Hash;
use bitcoin::hashes::Hash;
use bitcoin::secp256k1::XOnlyPublicKey;
use bitcoin::Network;
use error::Error;
#[cfg(feature = "nostr")]
use nostr_sdk::nips::nip04;
#[cfg(feature = "nostr")]
use nostr_sdk::{Filter, Timestamp};
use thiserror::Error;
use tokio::sync::RwLock;
use tracing::instrument;
use crate::amount::SplitTarget;
use crate::cdk_database::{self, WalletDatabase};
use crate::client::HttpClient;
use crate::dhke::{construct_proofs, hash_to_curve};
use crate::nuts::{
nut10, nut12, Conditions, CurrencyUnit, Id, KeySet, KeySetInfo, Keys, Kind,
@@ -32,78 +30,10 @@ use crate::nuts::{
use crate::types::{MeltQuote, Melted, MintQuote, ProofInfo};
use crate::url::UncheckedUrl;
use crate::util::{hex, unix_time};
use crate::{Amount, Bolt11Invoice};
use crate::{Amount, Bolt11Invoice, HttpClient};
#[derive(Debug, Error)]
pub enum Error {
/// Insufficient Funds
#[error("Insufficient Funds")]
InsufficientFunds,
#[error("Quote Expired")]
QuoteExpired,
#[error("Quote Unknown")]
QuoteUnknown,
#[error("No active keyset")]
NoActiveKeyset,
#[error(transparent)]
Cashu(#[from] crate::error::Error),
#[error("Could not verify Dleq")]
CouldNotVerifyDleq,
#[error("P2PK Condition Not met `{0}`")]
P2PKConditionsNotMet(String),
#[error("Invalid Spending Conditions: `{0}`")]
InvalidSpendConditions(String),
#[error("Preimage not provided")]
PreimageNotProvided,
#[error("Unknown Key")]
UnknownKey,
/// Spending Locktime not provided
#[error("Spending condition locktime not provided")]
LocktimeNotProvided,
/// Cashu Url Error
#[error(transparent)]
CashuUrl(#[from] crate::url::Error),
/// NUT11 Error
#[error(transparent)]
Client(#[from] crate::client::Error),
/// Database Error
#[error(transparent)]
Database(#[from] crate::cdk_database::Error),
/// NUT00 Error
#[error(transparent)]
NUT00(#[from] crate::nuts::nut00::Error),
/// NUT01 Error
#[error(transparent)]
NUT01(#[from] crate::nuts::nut01::Error),
/// NUT11 Error
#[error(transparent)]
NUT11(#[from] crate::nuts::nut11::Error),
/// NUT12 Error
#[error(transparent)]
NUT12(#[from] crate::nuts::nut12::Error),
/// Parse int
#[error(transparent)]
ParseInt(#[from] ParseIntError),
/// Parse invoice error
#[error(transparent)]
Invoice(#[from] lightning_invoice::ParseOrSemanticError),
#[error(transparent)]
Serde(#[from] serde_json::Error),
#[cfg(feature = "nostr")]
#[error(transparent)]
NostrClient(#[from] nostr_sdk::client::Error),
#[cfg(feature = "nostr")]
#[error(transparent)]
NostrKey(#[from] nostr_sdk::key::Error),
#[error("`{0}`")]
Custom(String),
}
impl From<Error> for cdk_database::Error {
fn from(e: Error) -> Self {
Self::Database(Box::new(e))
}
}
pub mod client;
pub mod error;
#[derive(Clone)]
pub struct Wallet {