feat: remove mint and wallet errors

This commit is contained in:
thesimplekid
2024-09-06 12:39:41 +01:00
parent 65c1aea408
commit 2fdf48cc72
23 changed files with 432 additions and 484 deletions

View File

@@ -59,13 +59,13 @@ pub async fn get_mint_bolt11_quote(
.ok_or_else(|| { .ok_or_else(|| {
tracing::info!("Bolt11 mint request for unsupported unit"); tracing::info!("Bolt11 mint request for unsupported unit");
into_response(Error::UnsupportedUnit) into_response(Error::UnitUnsupported)
})?; })?;
let amount = let amount =
to_unit(payload.amount, &payload.unit, &ln.get_settings().unit).map_err(|err| { to_unit(payload.amount, &payload.unit, &ln.get_settings().unit).map_err(|err| {
tracing::error!("Backed does not support unit: {}", err); tracing::error!("Backed does not support unit: {}", err);
into_response(Error::UnsupportedUnit) into_response(Error::UnitUnsupported)
})?; })?;
let quote_expiry = unix_time() + state.quote_ttl; let quote_expiry = unix_time() + state.quote_ttl;
@@ -139,7 +139,7 @@ pub async fn get_melt_bolt11_quote(
.ok_or_else(|| { .ok_or_else(|| {
tracing::info!("Could not get ln backend for {}, bolt11 ", payload.unit); tracing::info!("Could not get ln backend for {}, bolt11 ", payload.unit);
into_response(Error::UnsupportedUnit) into_response(Error::UnitUnsupported)
})?; })?;
let payment_quote = ln.get_payment_quote(&payload).await.map_err(|err| { let payment_quote = ln.get_payment_quote(&payload).await.map_err(|err| {
@@ -149,7 +149,7 @@ pub async fn get_melt_bolt11_quote(
err err
); );
into_response(Error::UnsupportedUnit) into_response(Error::UnitUnsupported)
})?; })?;
let quote = state let quote = state
@@ -199,7 +199,7 @@ pub async fn post_melt_bolt11(
if let Err(err) = state.mint.process_unpaid_melt(&payload).await { if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
tracing::error!("Could not reset melt quote state: {}", err); tracing::error!("Could not reset melt quote state: {}", err);
} }
return Err(into_response(Error::MeltRequestInvalid)); return Err(into_response(Error::UnitUnsupported));
} }
}; };
@@ -219,7 +219,7 @@ pub async fn post_melt_bolt11(
if let Err(err) = state.mint.process_unpaid_melt(&payload).await { if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
tracing::error!("Could not reset melt quote state: {}", err); tracing::error!("Could not reset melt quote state: {}", err);
} }
return Err(into_response(Error::DatabaseError)); return Err(into_response(Error::Internal));
} }
}; };
@@ -247,7 +247,7 @@ pub async fn post_melt_bolt11(
if let Err(err) = state.mint.process_unpaid_melt(&payload).await { if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
tracing::error!("Could not reset melt quote state: {}", err); tracing::error!("Could not reset melt quote state: {}", err);
} }
return Err(into_response(Error::InsufficientInputProofs)); return Err(into_response(Error::InsufficientFunds));
} }
mint_quote.state = MintQuoteState::Paid; mint_quote.state = MintQuoteState::Paid;
@@ -258,7 +258,7 @@ pub async fn post_melt_bolt11(
if let Err(err) = state.mint.process_unpaid_melt(&payload).await { if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
tracing::error!("Could not reset melt quote state: {}", err); tracing::error!("Could not reset melt quote state: {}", err);
} }
return Err(into_response(Error::DatabaseError)); return Err(into_response(Error::Internal));
} }
(None, amount) (None, amount)
@@ -305,7 +305,7 @@ pub async fn post_melt_bolt11(
Some( Some(
to_unit(partial_msats, &CurrencyUnit::Msat, &quote.unit) to_unit(partial_msats, &CurrencyUnit::Msat, &quote.unit)
.map_err(|_| into_response(Error::UnsupportedUnit))?, .map_err(|_| into_response(Error::UnitUnsupported))?,
) )
} }
false => None, false => None,
@@ -314,7 +314,7 @@ pub async fn post_melt_bolt11(
let amount_to_pay = match partial_amount { let amount_to_pay = match partial_amount {
Some(amount_to_pay) => amount_to_pay, Some(amount_to_pay) => amount_to_pay,
None => to_unit(invoice_amount_msats, &CurrencyUnit::Msat, &quote.unit) None => to_unit(invoice_amount_msats, &CurrencyUnit::Msat, &quote.unit)
.map_err(|_| into_response(Error::UnsupportedUnit))?, .map_err(|_| into_response(Error::UnitUnsupported))?,
}; };
if amount_to_pay + quote.fee_reserve > inputs_amount_quote_unit { if amount_to_pay + quote.fee_reserve > inputs_amount_quote_unit {
@@ -327,7 +327,11 @@ pub async fn post_melt_bolt11(
if let Err(err) = state.mint.process_unpaid_melt(&payload).await { if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
tracing::error!("Could not reset melt quote state: {}", err); tracing::error!("Could not reset melt quote state: {}", err);
} }
return Err(into_response(Error::InsufficientInputProofs)); return Err(into_response(Error::InsufficientInputs(
inputs_amount_quote_unit.into(),
amount_to_pay.into(),
quote.fee_reserve.into(),
)));
} }
} }
@@ -339,7 +343,7 @@ pub async fn post_melt_bolt11(
tracing::error!("Could not reset melt quote state: {}", err); tracing::error!("Could not reset melt quote state: {}", err);
} }
return Err(into_response(Error::UnsupportedUnit)); return Err(into_response(Error::UnitUnsupported));
} }
}; };
@@ -364,7 +368,7 @@ pub async fn post_melt_bolt11(
}; };
let amount_spent = to_unit(pre.total_spent, &ln.get_settings().unit, &quote.unit) let amount_spent = to_unit(pre.total_spent, &ln.get_settings().unit, &quote.unit)
.map_err(|_| into_response(Error::UnsupportedUnit))?; .map_err(|_| into_response(Error::UnitUnsupported))?;
(pre.payment_preimage, amount_spent) (pre.payment_preimage, amount_spent)
} }

View File

@@ -79,7 +79,7 @@ pub async fn start_mint(
) )
.await?; .await?;
let quote_ttl = 2000; let quote_ttl = 100000;
let mint_arc = Arc::new(mint); let mint_arc = Arc::new(mint);

View File

@@ -8,8 +8,8 @@ use bip39::Mnemonic;
use cdk::amount::SplitTarget; use cdk::amount::SplitTarget;
use cdk::cdk_database::WalletMemoryDatabase; use cdk::cdk_database::WalletMemoryDatabase;
use cdk::nuts::CurrencyUnit; use cdk::nuts::CurrencyUnit;
use cdk::wallet::error::Error;
use cdk::wallet::SendKind; use cdk::wallet::SendKind;
use cdk::Error;
use cdk::Wallet; use cdk::Wallet;
use cdk_integration_tests::{create_backends_fake_wallet, start_mint, wallet_mint, MINT_URL}; use cdk_integration_tests::{create_backends_fake_wallet, start_mint, wallet_mint, MINT_URL};
@@ -35,7 +35,8 @@ pub async fn test_mint_double_receive() -> Result<()> {
let wallet = Arc::new(wallet); let wallet = Arc::new(wallet);
wallet_mint(Arc::clone(&wallet), 100.into()).await?; wallet_mint(Arc::clone(&wallet), 100.into()).await.unwrap();
println!("Minted");
let token = wallet let token = wallet
.send( .send(
@@ -72,6 +73,7 @@ pub async fn test_mint_double_receive() -> Result<()> {
match err { match err {
Error::TokenAlreadySpent => (), Error::TokenAlreadySpent => (),
_ => { _ => {
println!("{}", err);
bail!("Expected an already spent error"); bail!("Expected an already spent error");
} }
} }

View File

@@ -5,6 +5,7 @@ use std::time::Duration;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use cdk::amount::SplitTarget; use cdk::amount::SplitTarget;
use cdk::nuts::{PreMintSecrets, SwapRequest}; use cdk::nuts::{PreMintSecrets, SwapRequest};
use cdk::Error;
use cdk::HttpClient; use cdk::HttpClient;
use cdk_integration_tests::{create_backends_fake_wallet, mint_proofs, start_mint, MINT_URL}; use cdk_integration_tests::{create_backends_fake_wallet, mint_proofs, start_mint, MINT_URL};
@@ -40,7 +41,7 @@ pub async fn test_unbalanced_swap() -> Result<()> {
// In the context of this test an error response here is good. // In the context of this test an error response here is good.
// It means the mint does not allow us to swap for more then we should by overflowing // It means the mint does not allow us to swap for more then we should by overflowing
Err(err) => match err { Err(err) => match err {
cdk::wallet::error::Error::TransactionUnbalanced => { Error::TransactionUnbalanced(_, _, _) => {
return Ok(()); return Ok(());
} }
_ => { _ => {

View File

@@ -40,12 +40,15 @@ pub enum Error {
/// CDK Error /// CDK Error
#[error(transparent)] #[error(transparent)]
CDK(#[from] cdk::error::Error), CDK(#[from] cdk::error::Error),
/// NUT02 Error
#[error(transparent)]
CDKNUT02(#[from] cdk::nuts::nut02::Error),
/// NUT00 Error /// NUT00 Error
#[error(transparent)] #[error(transparent)]
CDKNUT00(#[from] cdk::nuts::nut00::Error), CDKNUT00(#[from] cdk::nuts::nut00::Error),
/// NUT02 Error
#[error(transparent)]
CDKNUT02(#[from] cdk::nuts::nut02::Error),
/// DHKE Error
#[error(transparent)]
DHKE(#[from] cdk::dhke::Error),
/// Unknown Mint Info /// Unknown Mint Info
#[error("Unknown mint info")] #[error("Unknown mint info")]
UnknownMintInfo, UnknownMintInfo,

View File

@@ -11,9 +11,6 @@ pub enum Error {
/// Serde Error /// Serde Error
#[error(transparent)] #[error(transparent)]
Serde(#[from] serde_json::Error), Serde(#[from] serde_json::Error),
/// Wallet Error
#[error(transparent)]
CDKWallet(#[from] cdk::wallet::error::Error),
/// NUT00 Error /// NUT00 Error
#[error(transparent)] #[error(transparent)]
CDKNUT00(#[from] cdk::nuts::nut00::Error), CDKNUT00(#[from] cdk::nuts::nut00::Error),

View File

@@ -6,8 +6,18 @@ use std::cmp::Ordering;
use std::fmt; use std::fmt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::error::Error; /// Amount Error
#[derive(Debug, Error)]
pub enum Error {
/// Split Values must be less then or equal to amount
#[error("Split Values must be less then or equal to amount")]
SplitValuesGreater,
/// Amount overflow
#[error("Amount Overflow")]
AmountOverflow,
}
/// Amount can be any unit /// Amount can be any unit
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]

View File

@@ -43,9 +43,9 @@ pub enum Error {
/// Database Error /// Database Error
#[error(transparent)] #[error(transparent)]
Database(Box<dyn std::error::Error + Send + Sync>), Database(Box<dyn std::error::Error + Send + Sync>),
/// CDK Error /// DHKE error
#[error(transparent)] #[error(transparent)]
Cdk(#[from] crate::error::Error), DHKE(#[from] crate::dhke::Error),
/// NUT00 Error /// NUT00 Error
#[error(transparent)] #[error(transparent)]
NUT00(#[from] crate::nuts::nut00::Error), NUT00(#[from] crate::nuts::nut00::Error),

View File

@@ -7,8 +7,8 @@ use bitcoin::hashes::Hash;
use bitcoin::secp256k1::{ use bitcoin::secp256k1::{
Parity, PublicKey as NormalizedPublicKey, Scalar, Secp256k1, XOnlyPublicKey, Parity, PublicKey as NormalizedPublicKey, Scalar, Secp256k1, XOnlyPublicKey,
}; };
use thiserror::Error;
use crate::error::Error;
use crate::nuts::nut01::{PublicKey, SecretKey}; use crate::nuts::nut01::{PublicKey, SecretKey};
use crate::nuts::nut12::ProofDleq; use crate::nuts::nut12::ProofDleq;
use crate::nuts::{BlindSignature, Keys, Proof, Proofs}; use crate::nuts::{BlindSignature, Keys, Proof, Proofs};
@@ -18,6 +18,24 @@ use crate::SECP256K1;
const DOMAIN_SEPARATOR: &[u8; 28] = b"Secp256k1_HashToCurve_Cashu_"; const DOMAIN_SEPARATOR: &[u8; 28] = b"Secp256k1_HashToCurve_Cashu_";
/// NUT00 Error
#[derive(Debug, Error)]
pub enum Error {
/// Token could not be validated
#[error("Token not verified")]
TokenNotVerified,
/// No valid point on curve
#[error("No valid point found")]
NoValidPoint,
/// Secp256k1 error
#[error(transparent)]
Secp256k1(#[from] bitcoin::secp256k1::Error),
// TODO: Remove use anyhow
/// Custom Error
#[error("`{0}`")]
Custom(String),
}
/// Deterministically maps a message to a public key point on the secp256k1 /// Deterministically maps a message to a public key point on the secp256k1
/// curve, utilizing a domain separator to ensure uniqueness. /// curve, utilizing a domain separator to ensure uniqueness.
/// ///
@@ -103,7 +121,7 @@ pub fn construct_proofs(
keys: &Keys, keys: &Keys,
) -> Result<Proofs, Error> { ) -> Result<Proofs, Error> {
if (promises.len() != rs.len()) || (promises.len() != secrets.len()) { if (promises.len() != rs.len()) || (promises.len() != secrets.len()) {
return Err(Error::CustomError( return Err(Error::Custom(
"Lengths of promises, rs, and secrets must be equal".to_string(), "Lengths of promises, rs, and secrets must be equal".to_string(),
)); ));
} }
@@ -112,7 +130,7 @@ pub fn construct_proofs(
let blinded_c: PublicKey = blinded_signature.c; let blinded_c: PublicKey = blinded_signature.c;
let a: PublicKey = keys let a: PublicKey = keys
.amount_key(blinded_signature.amount) .amount_key(blinded_signature.amount)
.ok_or(Error::CustomError("Could not get proofs".to_string()))?; .ok_or(Error::Custom("Could not get proofs".to_string()))?;
let unblinded_signature: PublicKey = unblind_message(&blinded_c, &r, &a)?; let unblinded_signature: PublicKey = unblind_message(&blinded_c, &r, &a)?;

View File

@@ -1,13 +1,14 @@
//! Errors //! Errors
use std::fmt; use std::fmt;
use std::string::FromUtf8Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value; use serde_json::Value;
use thiserror::Error; use thiserror::Error;
use crate::util::hex; #[cfg(feature = "wallet")]
use crate::wallet::multi_mint_wallet::WalletKey;
use crate::{util::hex, Amount};
/// CDK Error /// CDK Error
#[derive(Debug, Error)] #[derive(Debug, Error)]
@@ -15,57 +16,146 @@ pub enum Error {
/// Mint does not have a key for amount /// Mint does not have a key for amount
#[error("No Key for Amount")] #[error("No Key for Amount")]
AmountKey, AmountKey,
/// Not enough input proofs provided
#[error("Not enough input proofs spent")]
InsufficientInputProofs,
/// Database update failed
#[error("Database error")]
DatabaseError,
/// Unsupported unit
#[error("Unit unsupported")]
UnsupportedUnit,
/// Payment failed /// Payment failed
#[error("Payment failed")] #[error("Payment failed")]
PaymentFailed, PaymentFailed,
/// Melt Request is not valid
#[error("Melt request is not valid")]
MeltRequestInvalid,
/// Invoice already paid /// Invoice already paid
#[error("Request already paid")] #[error("Request already paid")]
RequestAlreadyPaid, RequestAlreadyPaid,
/// 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")]
TokenNotVerified,
/// Invalid payment request /// Invalid payment request
#[error("Invalid payment request")] #[error("Invalid payment request")]
InvalidPaymentRequest, InvalidPaymentRequest,
/// Bolt11 invoice does not have amount /// Bolt11 invoice does not have amount
#[error("Invoice Amount undefined")] #[error("Invoice Amount undefined")]
InvoiceAmountUndefined, InvoiceAmountUndefined,
/// Proof is missing a required field
#[error("Proof missing required field")]
MissingProofField,
/// No valid point on curve
#[error("No valid point found")]
NoValidPoint,
/// Split Values must be less then or equal to amount /// Split Values must be less then or equal to amount
#[error("Split Values must be less then or equal to amount")] #[error("Split Values must be less then or equal to amount")]
SplitValuesGreater, SplitValuesGreater,
/// Amount overflow /// Amount overflow
#[error("Amount Overflow")] #[error("Amount Overflow")]
AmountOverflow, AmountOverflow,
/// Secp256k1 error
// Mint Errors
/// Minting is disabled
#[error("Minting is disabled")]
MintingDisabled,
/// Quote is not known
#[error("Unknown quote")]
UnknownQuote,
/// Quote is expired
#[error("Expired quote: Expired: `{0}`, Time: `{1}`")]
ExpiredQuote(u64, u64),
/// Amount is outside of allowed range
#[error("Amount but be between `{0}` and `{1}` is `{2}`")]
AmountOutofLimitRange(Amount, Amount, Amount),
/// Quote is not paiud
#[error("Quote not paid")]
UnpaidQuote,
/// Quote is pending
#[error("Quote pending")]
PendingQuote,
/// ecash already issued for quote
#[error("Quote already issued")]
IssuedQuote,
/// Quote has already been paid
#[error("Quote is already paid")]
PaidQuote,
/// Melting is disabled
#[error("Minting is disabled")]
MeltingDisabled,
/// Unknown Keyset
#[error("Unknown Keyset")]
UnknownKeySet,
/// BlindedMessage is already signed
#[error("Blinded Message is already signed")]
BlindedMessageAlreadySigned,
/// Inactive Keyset
#[error("Inactive Keyset")]
InactiveKeyset,
/// Not engough inputs provided
#[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
InsufficientInputs(u64, u64, u64),
/// Transaction unbalanced
#[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
TransactionUnbalanced(u64, u64, u64),
/// Duplicate proofs provided
#[error("Duplicate proofs")]
DuplicateProofs,
/// Multiple units provided
#[error("Cannot have multiple units")]
MultipleUnits,
/// Sig all cannot be used in melt
#[error("Sig all cannot be used in melt")]
SigAllUsedInMelt,
/// Token is already spent
#[error("Token Already Spent")]
TokenAlreadySpent,
/// Token is already pending
#[error("Token Pending")]
TokenPending,
/// Internal Error
#[error("Internal Error")]
Internal,
// Wallet Errors
/// P2PK spending conditions not met
#[error("P2PK condition not met `{0}`")]
P2PKConditionsNotMet(String),
/// Spending Locktime not provided
#[error("Spending condition locktime not provided")]
LocktimeNotProvided,
/// Invalid Spending Conditions
#[error("Invalid spending conditions: `{0}`")]
InvalidSpendConditions(String),
/// Incorrect Wallet
#[error("Incorrect wallet: `{0}`")]
IncorrectWallet(String),
/// Unknown Wallet
#[cfg(feature = "wallet")]
#[error("Unknown wallet: `{0}`")]
UnknownWallet(WalletKey),
/// Max Fee Ecxeded
#[error("Max fee exceeded")]
MaxFeeExceeded,
/// Url path segments could not be joined
#[error("Url path segments could not be joined")]
UrlPathSegments,
/// Unknown error response
#[error("Unknown error response: `{0}`")]
UnknownErrorResponse(String),
/// Invalid DLEQ proof
#[error("Could not verify DLEQ proof")]
CouldNotVerifyDleq,
/// Incorrect Mint
/// Token does not match wallet mint
#[error("Token does not match wallet mint")]
IncorrectMint,
/// Receive can only be used with tokens from single mint
#[error("Multiple mint tokens not supported by receive. Please deconstruct the token and use receive with_proof")]
MultiMintTokenNotSupported,
/// Unit Not supported
#[error("Unit not supported for method")]
UnitUnsupported,
/// Preimage not provided
#[error("Preimage not provided")]
PreimageNotProvided,
/// Insufficient Funds
#[error("Insufficient funds")]
InsufficientFunds,
/// No active keyset
#[error("No active keyset")]
NoActiveKeyset,
/// Incorrect quote amount
#[error("Incorrect quote amount")]
IncorrectQuoteAmount,
/// Custom Error
#[error("`{0}`")]
Custom(String),
// External Error conversions
/// Parse invoice error
#[error(transparent)] #[error(transparent)]
Secp256k1(#[from] bitcoin::secp256k1::Error), Invoice(#[from] lightning_invoice::ParseOrSemanticError),
/// Secret error
#[error(transparent)]
Secret(#[from] super::secret::Error),
/// Bip32 error /// Bip32 error
#[error(transparent)] #[error(transparent)]
Bip32(#[from] bitcoin::bip32::Error), Bip32(#[from] bitcoin::bip32::Error),
@@ -77,7 +167,7 @@ pub enum Error {
UrlParseError(#[from] url::ParseError), UrlParseError(#[from] url::ParseError),
/// Utf8 parse error /// Utf8 parse error
#[error(transparent)] #[error(transparent)]
Utf8ParseError(#[from] FromUtf8Error), Utf8ParseError(#[from] std::string::FromUtf8Error),
/// Serde Json error /// Serde Json error
#[error(transparent)] #[error(transparent)]
SerdeJsonError(#[from] serde_json::Error), SerdeJsonError(#[from] serde_json::Error),
@@ -91,18 +181,51 @@ pub enum Error {
/// From hex error /// From hex error
#[error(transparent)] #[error(transparent)]
ReqwestError(#[from] reqwest::Error), ReqwestError(#[from] reqwest::Error),
// Crate error conversions
/// Cashu Url Error
#[error(transparent)]
CashuUrl(#[from] crate::mint_url::Error),
/// Secret error
#[error(transparent)]
Secret(#[from] crate::secret::Error),
/// Amount Error
#[error(transparent)]
AmountError(#[from] crate::amount::Error),
/// DHKE Error
#[error(transparent)]
DHKE(#[from] crate::dhke::Error),
/// NUT00 Error
#[error(transparent)]
NUT00(#[from] crate::nuts::nut00::Error),
/// Nut01 error /// Nut01 error
#[error(transparent)] #[error(transparent)]
NUT01(#[from] crate::nuts::nut01::Error), NUT01(#[from] crate::nuts::nut01::Error),
/// NUT02 error /// NUT02 error
#[error(transparent)] #[error(transparent)]
NUT02(#[from] crate::nuts::nut02::Error), NUT02(#[from] crate::nuts::nut02::Error),
/// NUT03 error
#[error(transparent)]
NUT03(#[from] crate::nuts::nut03::Error),
/// NUT05 error
#[error(transparent)]
NUT05(#[from] crate::nuts::nut05::Error),
/// NUT11 Error /// NUT11 Error
#[error(transparent)] #[error(transparent)]
NUT11(#[from] crate::nuts::nut11::Error), NUT11(#[from] crate::nuts::nut11::Error),
/// Custom error /// NUT12 Error
#[error("`{0}`")] #[error(transparent)]
CustomError(String), NUT12(#[from] crate::nuts::nut12::Error),
/// NUT13 Error
#[error(transparent)]
NUT13(#[from] crate::nuts::nut13::Error),
/// NUT14 Error
#[error(transparent)]
NUT14(#[from] crate::nuts::nut14::Error),
/// Database Error
#[cfg(any(feature = "wallet", feature = "mint"))]
#[error(transparent)]
Database(#[from] crate::cdk_database::Error),
} }
/// CDK Error Response /// CDK Error Response
@@ -163,17 +286,7 @@ impl ErrorResponse {
impl From<Error> for ErrorResponse { impl From<Error> for ErrorResponse {
fn from(err: Error) -> ErrorResponse { fn from(err: Error) -> ErrorResponse {
match err { match err {
Error::TokenSpent => ErrorResponse { Error::UnitUnsupported => ErrorResponse {
code: ErrorCode::TokenAlreadySpent,
error: Some(err.to_string()),
detail: None,
},
Error::InsufficientInputProofs => ErrorResponse {
code: ErrorCode::InsufficientFee,
error: Some(err.to_string()),
detail: None,
},
Error::UnsupportedUnit => ErrorResponse {
code: ErrorCode::UnitUnsupported, code: ErrorCode::UnitUnsupported,
error: Some(err.to_string()), error: Some(err.to_string()),
detail: None, detail: None,
@@ -188,6 +301,51 @@ impl From<Error> for ErrorResponse {
error: Some("Invoice already paid.".to_string()), error: Some("Invoice already paid.".to_string()),
detail: None, detail: None,
}, },
Error::TokenAlreadySpent => ErrorResponse {
code: ErrorCode::TokenAlreadySpent,
error: Some("Token is already spent.".to_string()),
detail: None,
},
Error::TransactionUnbalanced(inputs_total, outputs_total, fee_expected) => {
ErrorResponse {
code: ErrorCode::TransactionUnbalanced,
error: Some(format!(
"Inputs: {}, Outputs: {}, expected_fee: {}",
inputs_total, outputs_total, fee_expected,
)),
detail: Some("Transaction inputs should equal outputs less fee".to_string()),
}
}
Error::MintingDisabled => ErrorResponse {
code: ErrorCode::MintingDisabled,
error: Some(err.to_string()),
detail: None,
},
Error::BlindedMessageAlreadySigned => ErrorResponse {
code: ErrorCode::BlindedMessageAlreadySigned,
error: Some(err.to_string()),
detail: None,
},
Error::InsufficientFunds => ErrorResponse {
code: ErrorCode::TransactionUnbalanced,
error: Some(err.to_string()),
detail: None,
},
Error::AmountOutofLimitRange(_min, _max, _amount) => ErrorResponse {
code: ErrorCode::AmountOutofLimitRange,
error: Some(err.to_string()),
detail: None,
},
Error::ExpiredQuote(_, _) => ErrorResponse {
code: ErrorCode::QuoteExpired,
error: Some(err.to_string()),
detail: None,
},
Error::PendingQuote => ErrorResponse {
code: ErrorCode::QuotePending,
error: Some(err.to_string()),
detail: None,
},
_ => ErrorResponse { _ => ErrorResponse {
code: ErrorCode::Unknown(9999), code: ErrorCode::Unknown(9999),
error: Some(err.to_string()), error: Some(err.to_string()),
@@ -197,6 +355,30 @@ impl From<Error> for ErrorResponse {
} }
} }
impl From<ErrorResponse> for Error {
fn from(err: ErrorResponse) -> Error {
match err.code {
ErrorCode::TokenAlreadySpent => Self::TokenAlreadySpent,
ErrorCode::QuoteNotPaid => Self::UnpaidQuote,
ErrorCode::QuotePending => Self::PendingQuote,
ErrorCode::QuoteExpired => Self::ExpiredQuote(0, 0),
ErrorCode::KeysetNotFound => Self::UnknownKeySet,
ErrorCode::KeysetInactive => Self::InactiveKeyset,
ErrorCode::BlindedMessageAlreadySigned => Self::BlindedMessageAlreadySigned,
ErrorCode::UnitUnsupported => Self::UnitUnsupported,
ErrorCode::TransactionUnbalanced => Self::TransactionUnbalanced(0, 0, 0),
ErrorCode::MintingDisabled => Self::MintingDisabled,
ErrorCode::InvoiceAlreadyPaid => Self::RequestAlreadyPaid,
ErrorCode::TokenNotVerified => Self::DHKE(crate::dhke::Error::TokenNotVerified),
ErrorCode::LightningError => Self::PaymentFailed,
ErrorCode::AmountOutofLimitRange => {
Self::AmountOutofLimitRange(Amount::default(), Amount::default(), Amount::default())
}
_ => Self::UnknownErrorResponse(err.to_string()),
}
}
}
/// Possible Error Codes /// Possible Error Codes
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum ErrorCode { pub enum ErrorCode {
@@ -204,14 +386,14 @@ pub enum ErrorCode {
TokenAlreadySpent, TokenAlreadySpent,
/// Quote is not paid /// Quote is not paid
QuoteNotPaid, QuoteNotPaid,
/// Quote is not expired
QuoteExpired,
/// Quote Pending
QuotePending,
/// Keyset is not found /// Keyset is not found
KeysetNotFound, KeysetNotFound,
/// Keyset inactive /// Keyset inactive
KeysetInactive, KeysetInactive,
/// Fee Overpaid
FeeOverPaid,
/// Insufficient Fee
InsufficientFee,
/// Blinded Message Already signed /// Blinded Message Already signed
BlindedMessageAlreadySigned, BlindedMessageAlreadySigned,
/// Unsupported unit /// Unsupported unit
@@ -220,8 +402,6 @@ pub enum ErrorCode {
TokensAlreadyIssued, TokensAlreadyIssued,
/// Minting Disabled /// Minting Disabled
MintingDisabled, MintingDisabled,
/// Quote Pending
QuotePending,
/// Invoice Already Paid /// Invoice Already Paid
InvoiceAlreadyPaid, InvoiceAlreadyPaid,
/// Token Not Verified /// Token Not Verified
@@ -230,6 +410,8 @@ pub enum ErrorCode {
LightningError, LightningError,
/// Unbalanced Error /// Unbalanced Error
TransactionUnbalanced, TransactionUnbalanced,
/// Amount outside of allowed range
AmountOutofLimitRange,
/// Unknown error code /// Unknown error code
Unknown(u16), Unknown(u16),
} }
@@ -243,8 +425,7 @@ impl ErrorCode {
11001 => Self::TokenAlreadySpent, 11001 => Self::TokenAlreadySpent,
11002 => Self::TransactionUnbalanced, 11002 => Self::TransactionUnbalanced,
11005 => Self::UnitUnsupported, 11005 => Self::UnitUnsupported,
11006 => Self::InsufficientFee, 11006 => Self::AmountOutofLimitRange,
11007 => Self::FeeOverPaid,
12001 => Self::KeysetNotFound, 12001 => Self::KeysetNotFound,
12002 => Self::KeysetInactive, 12002 => Self::KeysetInactive,
20000 => Self::LightningError, 20000 => Self::LightningError,
@@ -253,6 +434,7 @@ impl ErrorCode {
20003 => Self::MintingDisabled, 20003 => Self::MintingDisabled,
20005 => Self::QuotePending, 20005 => Self::QuotePending,
20006 => Self::InvoiceAlreadyPaid, 20006 => Self::InvoiceAlreadyPaid,
20007 => Self::QuoteExpired,
_ => Self::Unknown(code), _ => Self::Unknown(code),
} }
} }
@@ -265,8 +447,7 @@ impl ErrorCode {
Self::TokenAlreadySpent => 11001, Self::TokenAlreadySpent => 11001,
Self::TransactionUnbalanced => 11002, Self::TransactionUnbalanced => 11002,
Self::UnitUnsupported => 11005, Self::UnitUnsupported => 11005,
Self::InsufficientFee => 11006, Self::AmountOutofLimitRange => 11006,
Self::FeeOverPaid => 11007,
Self::KeysetNotFound => 12001, Self::KeysetNotFound => 12001,
Self::KeysetInactive => 12002, Self::KeysetInactive => 12002,
Self::LightningError => 20000, Self::LightningError => 20000,
@@ -275,6 +456,7 @@ impl ErrorCode {
Self::MintingDisabled => 20003, Self::MintingDisabled => 20003,
Self::QuotePending => 20005, Self::QuotePending => 20005,
Self::InvoiceAlreadyPaid => 20006, Self::InvoiceAlreadyPaid => 20006,
Self::QuoteExpired => 20007,
Self::Unknown(code) => *code, Self::Unknown(code) => *code,
} }
} }

View File

@@ -23,6 +23,8 @@ pub mod wallet;
#[doc(hidden)] #[doc(hidden)]
pub use bitcoin::secp256k1; pub use bitcoin::secp256k1;
#[doc(hidden)] #[doc(hidden)]
pub use error::Error;
#[doc(hidden)]
pub use lightning_invoice::{self, Bolt11Invoice}; pub use lightning_invoice::{self, Bolt11Invoice};
#[cfg(feature = "mint")] #[cfg(feature = "mint")]
#[doc(hidden)] #[doc(hidden)]

View File

@@ -1,208 +0,0 @@
//! Mint Errors
use thiserror::Error;
use crate::error::{ErrorCode, ErrorResponse};
use crate::{cdk_database, mint_url};
/// CDK Mint Error
#[derive(Debug, Error)]
pub enum Error {
/// Unknown Keyset
#[error("Unknown Keyset")]
UnknownKeySet,
/// Inactive Keyset
#[error("Inactive Keyset")]
InactiveKeyset,
/// There is not key for amount given
#[error("No key for amount")]
AmountKey,
/// Amount is not what is expected
#[error("Amount")]
Amount,
/// Amount overflow
#[error("Amount Overflow")]
AmountOverflow,
/// Not engough inputs provided
#[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
InsufficientInputs(u64, u64, u64),
/// Transaction unbalanced
#[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
TransactionUnbalanced(u64, u64, u64),
/// Duplicate proofs provided
#[error("Duplicate proofs")]
DuplicateProofs,
/// Token is already spent
#[error("Token Already Spent")]
TokenAlreadySpent,
/// Token is already pending
#[error("Token Pending")]
TokenPending,
/// Quote is not paiud
#[error("Quote not paid")]
UnpaidQuote,
/// Quote has already been paid
#[error("Quote is already paid")]
PaidQuote,
/// Quote is not known
#[error("Unknown quote")]
UnknownQuote,
/// Quote is pending
#[error("Quote pending")]
PendingQuote,
/// ecash already issued for quote
#[error("Quote already issued")]
IssuedQuote,
/// Unknown secret kind
#[error("Unknown secret kind")]
UnknownSecretKind,
/// Multiple units provided
#[error("Cannot have multiple units")]
MultipleUnits,
/// Unit not supported
#[error("Unit not supported")]
UnsupportedUnit,
/// BlindMessage is already signed
#[error("Blinded Message is already signed")]
BlindedMessageAlreadySigned,
/// Sig all cannot be used in melt
#[error("Sig all cannot be used in melt")]
SigAllUsedInMelt,
/// Minting is disabled
#[error("Minting is disabled")]
MintingDisabled,
/// Minting request is over mint limit
#[error("Mint request is over mint limit")]
MintOverLimit,
/// Mint request is uver mint limit
#[error("Mint request is under mint limit")]
MintUnderLimit,
/// Melting is disabled
#[error("Minting is disabled")]
MeltingDisabled,
/// Melting request is over mint limit
#[error("Mint request is over mint limit")]
MeltOverLimit,
/// Melt request is uver mint limit
#[error("Mint request is under mint limit")]
MeltUnderLimit,
/// Cashu Error
#[error(transparent)]
Cashu(#[from] crate::error::Error),
/// Secret Error
#[error(transparent)]
Secret(#[from] crate::secret::Error),
/// NUT00 Error
#[error(transparent)]
NUT00(#[from] crate::nuts::nut00::Error),
/// NUT04 Error
#[error(transparent)]
NUT04(#[from] crate::nuts::nut04::Error),
/// NUT05 Error
#[error(transparent)]
NUT05(#[from] crate::nuts::nut05::Error),
/// NUT11 Error
#[error(transparent)]
NUT11(#[from] crate::nuts::nut11::Error),
/// NUT12 Error
#[error(transparent)]
Nut12(#[from] crate::nuts::nut12::Error),
/// NUT14 Error
#[error(transparent)]
Nut14(#[from] crate::nuts::nut14::Error),
/// Database Error
#[error(transparent)]
Database(#[from] cdk_database::Error),
/// Mint Url Error
#[error(transparent)]
MintUrl(#[from] mint_url::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<Error> for ErrorResponse {
fn from(err: Error) -> ErrorResponse {
match err {
Error::TokenAlreadySpent => ErrorResponse {
code: ErrorCode::TokenAlreadySpent,
error: Some(err.to_string()),
detail: None,
},
Error::TransactionUnbalanced(_inputs_amount, _output_amouns, _expected_fee) => {
ErrorResponse {
code: ErrorCode::TransactionUnbalanced,
error: Some(err.to_string()),
detail: None,
}
}
Error::InsufficientInputs(_inputs_amount, _output_amount, _expected_fee) => {
ErrorResponse {
code: ErrorCode::InsufficientFee,
error: Some(err.to_string()),
detail: None,
}
}
Error::MintingDisabled => ErrorResponse {
code: ErrorCode::MintingDisabled,
error: Some(err.to_string()),
detail: None,
},
Error::InactiveKeyset => ErrorResponse {
code: ErrorCode::KeysetInactive,
error: Some(err.to_string()),
detail: None,
},
Error::UnknownKeySet => ErrorResponse {
code: ErrorCode::KeysetNotFound,
error: Some(err.to_string()),
detail: None,
},
Error::UnpaidQuote => ErrorResponse {
code: ErrorCode::QuoteNotPaid,
error: Some(err.to_string()),
detail: None,
},
Error::PendingQuote => ErrorResponse {
code: ErrorCode::QuotePending,
error: Some(err.to_string()),
detail: None,
},
Error::IssuedQuote => ErrorResponse {
code: ErrorCode::TokensAlreadyIssued,
error: Some(err.to_string()),
detail: None,
},
_ => ErrorResponse {
code: ErrorCode::Unknown(9999),
error: Some(err.to_string()),
detail: None,
},
}
}
}
#[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

@@ -6,7 +6,6 @@ use std::sync::Arc;
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey}; use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
use bitcoin::secp256k1::{self, Secp256k1}; use bitcoin::secp256k1::{self, Secp256k1};
use error::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tracing::instrument; use tracing::instrument;
@@ -15,13 +14,13 @@ use self::nut05::QuoteState;
use self::nut11::EnforceSigFlag; use self::nut11::EnforceSigFlag;
use crate::cdk_database::{self, MintDatabase}; use crate::cdk_database::{self, MintDatabase};
use crate::dhke::{hash_to_curve, sign_message, verify_message}; use crate::dhke::{hash_to_curve, sign_message, verify_message};
use crate::error::Error;
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
use crate::nuts::nut11::enforce_sig_flag; use crate::nuts::nut11::enforce_sig_flag;
use crate::nuts::*; use crate::nuts::*;
use crate::util::unix_time; use crate::util::unix_time;
use crate::Amount; use crate::Amount;
pub mod error;
pub mod types; pub mod types;
pub use types::{MeltQuote, MintQuote}; pub use types::{MeltQuote, MintQuote};
@@ -212,18 +211,26 @@ impl Mint {
.max_amount .max_amount
.map_or(false, |max_amount| amount > max_amount) .map_or(false, |max_amount| amount > max_amount)
{ {
return Err(Error::MintOverLimit); return Err(Error::AmountOutofLimitRange(
settings.min_amount.unwrap_or_default(),
settings.max_amount.unwrap_or_default(),
amount,
));
} }
if settings if settings
.min_amount .min_amount
.map_or(false, |min_amount| amount < min_amount) .map_or(false, |min_amount| amount < min_amount)
{ {
return Err(Error::MintUnderLimit); return Err(Error::AmountOutofLimitRange(
settings.min_amount.unwrap_or_default(),
settings.max_amount.unwrap_or_default(),
amount,
));
} }
} }
None => { None => {
return Err(Error::UnsupportedUnit); return Err(Error::UnitUnsupported);
} }
} }
@@ -359,18 +366,26 @@ impl Mint {
.max_amount .max_amount
.map_or(false, |max_amount| amount > max_amount) .map_or(false, |max_amount| amount > max_amount)
{ {
return Err(Error::MeltOverLimit); return Err(Error::AmountOutofLimitRange(
settings.min_amount.unwrap_or_default(),
settings.max_amount.unwrap_or_default(),
amount,
));
} }
if settings if settings
.min_amount .min_amount
.map_or(false, |min_amount| amount < min_amount) .map_or(false, |min_amount| amount < min_amount)
{ {
return Err(Error::MeltUnderLimit); return Err(Error::AmountOutofLimitRange(
settings.min_amount.unwrap_or_default(),
settings.max_amount.unwrap_or_default(),
amount,
));
} }
} }
None => { None => {
return Err(Error::UnsupportedUnit); return Err(Error::UnitUnsupported);
} }
} }
@@ -555,6 +570,18 @@ impl Mint {
&self, &self,
mint_request: nut04::MintBolt11Request, mint_request: nut04::MintBolt11Request,
) -> Result<nut04::MintBolt11Response, Error> { ) -> Result<nut04::MintBolt11Response, Error> {
// Check quote is known and not expired
match self.localstore.get_mint_quote(&mint_request.quote).await? {
Some(quote) => {
if quote.expiry < unix_time() {
return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
}
}
None => {
return Err(Error::UnknownQuote);
}
}
let state = self let state = self
.localstore .localstore
.update_mint_quote_state(&mint_request.quote, MintQuoteState::Pending) .update_mint_quote_state(&mint_request.quote, MintQuoteState::Pending)

View File

@@ -59,9 +59,18 @@ pub enum Error {
/// Ciborium error /// Ciborium error
#[error(transparent)] #[error(transparent)]
CiboriumError(#[from] ciborium::de::Error<std::io::Error>), CiboriumError(#[from] ciborium::de::Error<std::io::Error>),
/// CDK error /// Amount Error
#[error(transparent)] #[error(transparent)]
Cdk(#[from] crate::error::Error), Amount(#[from] crate::amount::Error),
/// Secret error
#[error(transparent)]
Secret(#[from] crate::secret::Error),
/// DHKE error
#[error(transparent)]
DHKE(#[from] crate::dhke::Error),
/// NUT10 error
#[error(transparent)]
NUT10(#[from] crate::nuts::nut10::Error),
/// NUT11 error /// NUT11 error
#[error(transparent)] #[error(transparent)]
NUT11(#[from] crate::nuts::nut11::Error), NUT11(#[from] crate::nuts::nut11::Error),

View File

@@ -3,9 +3,21 @@
//! <https://github.com/cashubtc/nuts/blob/main/03.md> //! <https://github.com/cashubtc/nuts/blob/main/03.md>
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs}; use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs};
use crate::{error::Error, Amount}; use crate::Amount;
/// NUT03 Error
#[derive(Debug, Error)]
pub enum Error {
/// DHKE error
#[error(transparent)]
DHKE(#[from] crate::dhke::Error),
/// Amount Error
#[error(transparent)]
Amount(#[from] crate::amount::Error),
}
/// Preswap information /// Preswap information
#[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
@@ -37,12 +49,16 @@ impl SwapRequest {
/// Total value of proofs in [`SwapRequest`] /// Total value of proofs in [`SwapRequest`]
pub fn input_amount(&self) -> Result<Amount, Error> { pub fn input_amount(&self) -> Result<Amount, Error> {
Amount::try_sum(self.inputs.iter().map(|proof| proof.amount)) Ok(Amount::try_sum(
self.inputs.iter().map(|proof| proof.amount),
)?)
} }
/// Total value of outputs in [`SwapRequest`] /// Total value of outputs in [`SwapRequest`]
pub fn output_amount(&self) -> Result<Amount, Error> { pub fn output_amount(&self) -> Result<Amount, Error> {
Amount::try_sum(self.outputs.iter().map(|proof| proof.amount)) Ok(Amount::try_sum(
self.outputs.iter().map(|proof| proof.amount),
)?)
} }
} }
@@ -63,10 +79,10 @@ impl SwapResponse {
/// Total [`Amount`] of promises /// Total [`Amount`] of promises
pub fn promises_amount(&self) -> Result<Amount, Error> { pub fn promises_amount(&self) -> Result<Amount, Error> {
Amount::try_sum( Ok(Amount::try_sum(
self.signatures self.signatures
.iter() .iter()
.map(|BlindSignature { amount, .. }| *amount), .map(|BlindSignature { amount, .. }| *amount),
) )?)
} }
} }

View File

@@ -6,8 +6,18 @@ use std::str::FromStr;
use serde::ser::SerializeTuple; use serde::ser::SerializeTuple;
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use thiserror::Error;
use crate::error::Error; /// NUT13 Error
#[derive(Debug, Error)]
pub enum Error {
/// Secret error
#[error(transparent)]
Secret(#[from] crate::secret::Error),
/// Serde Json error
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
}
/// NUT10 Secret Kind /// NUT10 Secret Kind
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]

View File

@@ -26,9 +26,9 @@ pub enum Error {
/// Invalid DLEQ Proof /// Invalid DLEQ Proof
#[error("Invalid DLEQ proof")] #[error("Invalid DLEQ proof")]
InvalidDleqProof, InvalidDleqProof,
/// Cashu Error /// DHKE error
#[error(transparent)] #[error(transparent)]
Cashu(#[from] crate::error::Error), DHKE(#[from] crate::dhke::Error),
/// NUT01 Error /// NUT01 Error
#[error(transparent)] #[error(transparent)]
NUT01(#[from] crate::nuts::nut01::Error), NUT01(#[from] crate::nuts::nut01::Error),

View File

@@ -3,6 +3,7 @@
//! <https://github.com/cashubtc/nuts/blob/main/13.md> //! <https://github.com/cashubtc/nuts/blob/main/13.md>
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey}; use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
use thiserror::Error;
use tracing::instrument; use tracing::instrument;
use super::nut00::{BlindedMessage, PreMint, PreMintSecrets}; use super::nut00::{BlindedMessage, PreMint, PreMintSecrets};
@@ -10,11 +11,30 @@ use super::nut01::SecretKey;
use super::nut02::Id; use super::nut02::Id;
use crate::amount::SplitTarget; use crate::amount::SplitTarget;
use crate::dhke::blind_message; use crate::dhke::blind_message;
use crate::error::Error;
use crate::secret::Secret; use crate::secret::Secret;
use crate::util::hex; use crate::util::hex;
use crate::{Amount, SECP256K1}; use crate::{Amount, SECP256K1};
/// NUT13 Error
#[derive(Debug, Error)]
pub enum Error {
/// DHKE error
#[error(transparent)]
DHKE(#[from] crate::dhke::Error),
/// Amount Error
#[error(transparent)]
Amount(#[from] crate::amount::Error),
/// NUT00 Error
#[error(transparent)]
NUT00(#[from] crate::nuts::nut00::Error),
/// NUT02 Error
#[error(transparent)]
NUT02(#[from] crate::nuts::nut02::Error),
/// Bip32 Error
#[error(transparent)]
Bip32(#[from] bitcoin::bip32::Error),
}
impl Secret { impl Secret {
/// Create new [`Secret`] from xpriv /// Create new [`Secret`] from xpriv
pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> { pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {

View File

@@ -23,6 +23,9 @@ pub enum Error {
/// Hex Error /// Hex Error
#[error(transparent)] #[error(transparent)]
Hex(#[from] hex::Error), Hex(#[from] hex::Error),
/// Serde Json error
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
} }
impl Default for Secret { impl Default for Secret {
@@ -119,10 +122,10 @@ impl TryFrom<Secret> for crate::nuts::nut10::Secret {
} }
impl TryFrom<&Secret> for crate::nuts::nut10::Secret { impl TryFrom<&Secret> for crate::nuts::nut10::Secret {
type Error = serde_json::Error; type Error = Error;
fn try_from(unchecked_secret: &Secret) -> Result<crate::nuts::nut10::Secret, Self::Error> { fn try_from(unchecked_secret: &Secret) -> Result<crate::nuts::nut10::Secret, Self::Error> {
serde_json::from_str(&unchecked_secret.0) Ok(serde_json::from_str(&unchecked_secret.0)?)
} }
} }

View File

@@ -44,9 +44,7 @@ impl ProofInfo {
state: State, state: State,
unit: CurrencyUnit, unit: CurrencyUnit,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let y = proof let y = proof.y()?;
.y()
.map_err(|_| Error::CustomError("Could not find y".to_string()))?;
let spending_condition: Option<SpendingConditions> = (&proof.secret).try_into().ok(); let spending_condition: Option<SpendingConditions> = (&proof.secret).try_into().ok();

View File

@@ -1,148 +0,0 @@
//! CDK Wallet Error
use std::num::ParseIntError;
use thiserror::Error;
use super::multi_mint_wallet::WalletKey;
use crate::cdk_database;
use crate::error::{ErrorCode, ErrorResponse};
use crate::util::hex;
/// Wallet Error
#[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 proof
#[error("Could not verify DLEQ proof")]
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,
/// Unknown Key
#[error("Unknown key")]
UnknownKey,
/// Amount overflow
#[error("Amount Overflow")]
AmountOverflow,
/// Spending Locktime not provided
#[error("Spending condition locktime not provided")]
LocktimeNotProvided,
/// Url path segments could not be joined
#[error("Url path segments could not be joined")]
UrlPathSegments,
/// Quote not paid
#[error("Quote not paid")]
QuoteNotePaid,
/// Token Already Spent
#[error("Token already spent")]
TokenAlreadySpent,
/// Unit Not supported
#[error("Unit not supported for method")]
UnitNotSupported,
/// Bolt11 invoice does not have amount
#[error("Invoice amount undefined")]
InvoiceAmountUndefined,
/// Incorrect quote amount
#[error("Incorrect quote amount")]
IncorrectQuoteAmount,
/// Keyset Not Found
#[error("Keyset not found")]
KeysetNotFound,
/// Receive can only be used with tokens from single mint
#[error("Multiple mint tokens not supported by receive. Please deconstruct the token and use receive with_proof")]
MultiMintTokenNotSupported,
/// Incorrect Mint
/// Token does not match wallet mint
#[error("Token does not match wallet mint")]
IncorrectMint,
/// From hex error
#[error(transparent)]
ReqwestError(#[from] reqwest::Error),
/// Unknown error response
#[error("Unknown error response: `{0}`")]
UnknownErrorResponse(String),
/// Hex Error
#[error(transparent)]
HexError(#[from] hex::Error),
/// Unknown Wallet
#[error("Unknown wallet: `{0}`")]
UnknownWallet(WalletKey),
/// Incorrect Wallet
#[error("Incorrect wallet: `{0}`")]
IncorrectWallet(String),
/// Max Fee Ecxeded
#[error("Max fee exceeded")]
MaxFeeExceeded,
/// Transaction unbalanced
#[error("Transaction is unbalanced")]
TransactionUnbalanced,
/// CDK Error
#[error(transparent)]
Cashu(#[from] crate::error::Error),
/// Cashu Url Error
#[error(transparent)]
CashuUrl(#[from] crate::mint_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),
/// 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,
ErrorCode::TransactionUnbalanced => Self::TransactionUnbalanced,
_ => Self::UnknownErrorResponse(err.to_string()),
}
}
}

View File

@@ -9,12 +9,12 @@ use bitcoin::hashes::sha256::Hash as Sha256Hash;
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use bitcoin::key::XOnlyPublicKey; use bitcoin::key::XOnlyPublicKey;
use bitcoin::Network; use bitcoin::Network;
use error::Error;
use tracing::instrument; use tracing::instrument;
use crate::amount::SplitTarget; use crate::amount::SplitTarget;
use crate::cdk_database::{self, WalletDatabase}; use crate::cdk_database::{self, WalletDatabase};
use crate::dhke::{construct_proofs, hash_to_curve}; use crate::dhke::{construct_proofs, hash_to_curve};
use crate::error::Error;
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
use crate::nuts::nut00::token::Token; use crate::nuts::nut00::token::Token;
use crate::nuts::{ use crate::nuts::{
@@ -28,7 +28,6 @@ use crate::util::{hex, unix_time};
use crate::{Amount, Bolt11Invoice, HttpClient, SECP256K1}; use crate::{Amount, Bolt11Invoice, HttpClient, SECP256K1};
pub mod client; pub mod client;
pub mod error;
pub mod multi_mint_wallet; pub mod multi_mint_wallet;
pub mod types; pub mod types;
pub mod util; pub mod util;
@@ -108,7 +107,7 @@ impl Wallet {
.localstore .localstore
.get_keyset_by_id(&proof.keyset_id) .get_keyset_by_id(&proof.keyset_id)
.await? .await?
.ok_or(Error::UnknownKey)?; .ok_or(Error::UnknownKeySet)?;
sum_fee += input_fee_ppk.input_fee_ppk; sum_fee += input_fee_ppk.input_fee_ppk;
} }
@@ -125,7 +124,7 @@ impl Wallet {
.localstore .localstore
.get_keyset_by_id(keyset_id) .get_keyset_by_id(keyset_id)
.await? .await?
.ok_or(Error::UnknownKey)? .ok_or(Error::UnknownKeySet)?
.input_fee_ppk; .input_fee_ppk;
let fee = (input_fee_ppk * count + 999) / 1000; let fee = (input_fee_ppk * count + 999) / 1000;
@@ -615,12 +614,12 @@ impl Wallet {
let quote_info = if let Some(quote) = quote_info { let quote_info = if let Some(quote) = quote_info {
if quote.expiry.le(&unix_time()) && quote.expiry.ne(&0) { if quote.expiry.le(&unix_time()) && quote.expiry.ne(&0) {
return Err(Error::QuoteExpired); return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
} }
quote.clone() quote.clone()
} else { } else {
return Err(Error::QuoteUnknown); return Err(Error::UnknownQuote);
}; };
let active_keyset_id = self.get_active_mint_keyset().await?.id; let active_keyset_id = self.get_active_mint_keyset().await?.id;
@@ -663,7 +662,7 @@ impl Wallet {
{ {
for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) { for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
let keys = self.get_keyset_keys(sig.keyset_id).await?; let keys = self.get_keyset_keys(sig.keyset_id).await?;
let key = keys.amount_key(sig.amount).ok_or(Error::UnknownKey)?; let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
match sig.verify_dleq(key, premint.blinded_message.blinded_secret) { match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
Ok(_) | Err(nut12::Error::MissingDleqProof) => (), Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
Err(_) => return Err(Error::CouldNotVerifyDleq), Err(_) => return Err(Error::CouldNotVerifyDleq),
@@ -1292,7 +1291,7 @@ impl Wallet {
let amount = match self.unit { let amount = match self.unit {
CurrencyUnit::Sat => Amount::from(request_amount / 1000), CurrencyUnit::Sat => Amount::from(request_amount / 1000),
CurrencyUnit::Msat => Amount::from(request_amount), CurrencyUnit::Msat => Amount::from(request_amount),
_ => return Err(Error::UnitNotSupported), _ => return Err(Error::UnitUnsupported),
}; };
let quote_res = self let quote_res = self
@@ -1352,12 +1351,12 @@ impl Wallet {
let quote_info = self.localstore.get_melt_quote(quote_id).await?; let quote_info = self.localstore.get_melt_quote(quote_id).await?;
let quote_info = if let Some(quote) = quote_info { let quote_info = if let Some(quote) = quote_info {
if quote.expiry.le(&unix_time()) { if quote.expiry.le(&unix_time()) {
return Err(Error::QuoteExpired); return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
} }
quote.clone() quote.clone()
} else { } else {
return Err(Error::QuoteUnknown); return Err(Error::UnknownQuote);
}; };
let ys = proofs let ys = proofs
@@ -1503,12 +1502,12 @@ impl Wallet {
let quote_info = if let Some(quote) = quote_info { let quote_info = if let Some(quote) = quote_info {
if quote.expiry.le(&unix_time()) { if quote.expiry.le(&unix_time()) {
return Err(Error::QuoteExpired); return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
} }
quote.clone() quote.clone()
} else { } else {
return Err(Error::QuoteUnknown); return Err(Error::UnknownQuote);
}; };
let inputs_needed_amount = quote_info.amount + quote_info.fee_reserve; let inputs_needed_amount = quote_info.amount + quote_info.fee_reserve;
@@ -1684,7 +1683,7 @@ impl Wallet {
// Verify that proof DLEQ is valid // Verify that proof DLEQ is valid
if proof.dleq.is_some() { if proof.dleq.is_some() {
let keys = self.get_keyset_keys(proof.keyset_id).await?; let keys = self.get_keyset_keys(proof.keyset_id).await?;
let key = keys.amount_key(proof.amount).ok_or(Error::UnknownKey)?; let key = keys.amount_key(proof.amount).ok_or(Error::AmountKey)?;
proof.verify_dleq(key)?; proof.verify_dleq(key)?;
} }
@@ -1815,7 +1814,7 @@ impl Wallet {
let unit = token_data.unit().unwrap_or_default(); let unit = token_data.unit().unwrap_or_default();
if unit != self.unit { if unit != self.unit {
return Err(Error::UnitNotSupported); return Err(Error::UnitUnsupported);
} }
let proofs = token_data.proofs(); let proofs = token_data.proofs();
@@ -2108,7 +2107,7 @@ impl Wallet {
key key
} }
} }
.ok_or(Error::UnknownKey)?; .ok_or(Error::AmountKey)?;
proof proof
.verify_dleq(mint_pubkey) .verify_dleq(mint_pubkey)

View File

@@ -7,7 +7,7 @@ alias t := test
default: default:
@just --list @just --list
final-check: format clippy test final-check: typos format clippy test
# run `cargo build` on everything # run `cargo build` on everything
build *ARGS="--workspace --all-targets": build *ARGS="--workspace --all-targets":
@@ -54,6 +54,9 @@ clippy *ARGS="--locked --offline --workspace --all-targets":
clippy-fix *ARGS="--locked --offline --workspace --all-targets": clippy-fix *ARGS="--locked --offline --workspace --all-targets":
cargo clippy {{ARGS}} --fix cargo clippy {{ARGS}} --fix
typos:
typos
# fix all typos # fix all typos
[no-exit-message] [no-exit-message]
typos-fix: typos-fix: