mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-19 13:44:55 +01:00
feat: remove mint and wallet errors
This commit is contained in:
@@ -59,13 +59,13 @@ pub async fn get_mint_bolt11_quote(
|
||||
.ok_or_else(|| {
|
||||
tracing::info!("Bolt11 mint request for unsupported unit");
|
||||
|
||||
into_response(Error::UnsupportedUnit)
|
||||
into_response(Error::UnitUnsupported)
|
||||
})?;
|
||||
|
||||
let amount =
|
||||
to_unit(payload.amount, &payload.unit, &ln.get_settings().unit).map_err(|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;
|
||||
@@ -139,7 +139,7 @@ pub async fn get_melt_bolt11_quote(
|
||||
.ok_or_else(|| {
|
||||
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| {
|
||||
@@ -149,7 +149,7 @@ pub async fn get_melt_bolt11_quote(
|
||||
err
|
||||
);
|
||||
|
||||
into_response(Error::UnsupportedUnit)
|
||||
into_response(Error::UnitUnsupported)
|
||||
})?;
|
||||
|
||||
let quote = state
|
||||
@@ -199,7 +199,7 @@ pub async fn post_melt_bolt11(
|
||||
if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
|
||||
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 {
|
||||
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 {
|
||||
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;
|
||||
@@ -258,7 +258,7 @@ pub async fn post_melt_bolt11(
|
||||
if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
|
||||
tracing::error!("Could not reset melt quote state: {}", err);
|
||||
}
|
||||
return Err(into_response(Error::DatabaseError));
|
||||
return Err(into_response(Error::Internal));
|
||||
}
|
||||
|
||||
(None, amount)
|
||||
@@ -305,7 +305,7 @@ pub async fn post_melt_bolt11(
|
||||
|
||||
Some(
|
||||
to_unit(partial_msats, &CurrencyUnit::Msat, "e.unit)
|
||||
.map_err(|_| into_response(Error::UnsupportedUnit))?,
|
||||
.map_err(|_| into_response(Error::UnitUnsupported))?,
|
||||
)
|
||||
}
|
||||
false => None,
|
||||
@@ -314,7 +314,7 @@ pub async fn post_melt_bolt11(
|
||||
let amount_to_pay = match partial_amount {
|
||||
Some(amount_to_pay) => amount_to_pay,
|
||||
None => to_unit(invoice_amount_msats, &CurrencyUnit::Msat, "e.unit)
|
||||
.map_err(|_| into_response(Error::UnsupportedUnit))?,
|
||||
.map_err(|_| into_response(Error::UnitUnsupported))?,
|
||||
};
|
||||
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
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, "e.unit)
|
||||
.map_err(|_| into_response(Error::UnsupportedUnit))?;
|
||||
.map_err(|_| into_response(Error::UnitUnsupported))?;
|
||||
|
||||
(pre.payment_preimage, amount_spent)
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ pub async fn start_mint(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let quote_ttl = 2000;
|
||||
let quote_ttl = 100000;
|
||||
|
||||
let mint_arc = Arc::new(mint);
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ use bip39::Mnemonic;
|
||||
use cdk::amount::SplitTarget;
|
||||
use cdk::cdk_database::WalletMemoryDatabase;
|
||||
use cdk::nuts::CurrencyUnit;
|
||||
use cdk::wallet::error::Error;
|
||||
use cdk::wallet::SendKind;
|
||||
use cdk::Error;
|
||||
use cdk::Wallet;
|
||||
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);
|
||||
|
||||
wallet_mint(Arc::clone(&wallet), 100.into()).await?;
|
||||
wallet_mint(Arc::clone(&wallet), 100.into()).await.unwrap();
|
||||
println!("Minted");
|
||||
|
||||
let token = wallet
|
||||
.send(
|
||||
@@ -72,6 +73,7 @@ pub async fn test_mint_double_receive() -> Result<()> {
|
||||
match err {
|
||||
Error::TokenAlreadySpent => (),
|
||||
_ => {
|
||||
println!("{}", err);
|
||||
bail!("Expected an already spent error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::time::Duration;
|
||||
use anyhow::{bail, Result};
|
||||
use cdk::amount::SplitTarget;
|
||||
use cdk::nuts::{PreMintSecrets, SwapRequest};
|
||||
use cdk::Error;
|
||||
use cdk::HttpClient;
|
||||
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.
|
||||
// It means the mint does not allow us to swap for more then we should by overflowing
|
||||
Err(err) => match err {
|
||||
cdk::wallet::error::Error::TransactionUnbalanced => {
|
||||
Error::TransactionUnbalanced(_, _, _) => {
|
||||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -40,12 +40,15 @@ pub enum Error {
|
||||
/// CDK Error
|
||||
#[error(transparent)]
|
||||
CDK(#[from] cdk::error::Error),
|
||||
/// NUT02 Error
|
||||
#[error(transparent)]
|
||||
CDKNUT02(#[from] cdk::nuts::nut02::Error),
|
||||
/// NUT00 Error
|
||||
#[error(transparent)]
|
||||
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
|
||||
#[error("Unknown mint info")]
|
||||
UnknownMintInfo,
|
||||
|
||||
@@ -11,9 +11,6 @@ pub enum Error {
|
||||
/// Serde Error
|
||||
#[error(transparent)]
|
||||
Serde(#[from] serde_json::Error),
|
||||
/// Wallet Error
|
||||
#[error(transparent)]
|
||||
CDKWallet(#[from] cdk::wallet::error::Error),
|
||||
/// NUT00 Error
|
||||
#[error(transparent)]
|
||||
CDKNUT00(#[from] cdk::nuts::nut00::Error),
|
||||
|
||||
@@ -6,8 +6,18 @@ use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
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
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
|
||||
@@ -43,9 +43,9 @@ pub enum Error {
|
||||
/// Database Error
|
||||
#[error(transparent)]
|
||||
Database(Box<dyn std::error::Error + Send + Sync>),
|
||||
/// CDK Error
|
||||
/// DHKE error
|
||||
#[error(transparent)]
|
||||
Cdk(#[from] crate::error::Error),
|
||||
DHKE(#[from] crate::dhke::Error),
|
||||
/// NUT00 Error
|
||||
#[error(transparent)]
|
||||
NUT00(#[from] crate::nuts::nut00::Error),
|
||||
|
||||
@@ -7,8 +7,8 @@ use bitcoin::hashes::Hash;
|
||||
use bitcoin::secp256k1::{
|
||||
Parity, PublicKey as NormalizedPublicKey, Scalar, Secp256k1, XOnlyPublicKey,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::nuts::nut01::{PublicKey, SecretKey};
|
||||
use crate::nuts::nut12::ProofDleq;
|
||||
use crate::nuts::{BlindSignature, Keys, Proof, Proofs};
|
||||
@@ -18,6 +18,24 @@ use crate::SECP256K1;
|
||||
|
||||
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
|
||||
/// curve, utilizing a domain separator to ensure uniqueness.
|
||||
///
|
||||
@@ -103,7 +121,7 @@ pub fn construct_proofs(
|
||||
keys: &Keys,
|
||||
) -> Result<Proofs, Error> {
|
||||
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(),
|
||||
));
|
||||
}
|
||||
@@ -112,7 +130,7 @@ pub fn construct_proofs(
|
||||
let blinded_c: PublicKey = blinded_signature.c;
|
||||
let a: PublicKey = keys
|
||||
.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)?;
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
//! Errors
|
||||
|
||||
use std::fmt;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json::Value;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::util::hex;
|
||||
#[cfg(feature = "wallet")]
|
||||
use crate::wallet::multi_mint_wallet::WalletKey;
|
||||
use crate::{util::hex, Amount};
|
||||
|
||||
/// CDK Error
|
||||
#[derive(Debug, Error)]
|
||||
@@ -15,57 +16,146 @@ pub enum Error {
|
||||
/// Mint does not have a key for amount
|
||||
#[error("No Key for Amount")]
|
||||
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
|
||||
#[error("Payment failed")]
|
||||
PaymentFailed,
|
||||
/// Melt Request is not valid
|
||||
#[error("Melt request is not valid")]
|
||||
MeltRequestInvalid,
|
||||
/// Invoice already paid
|
||||
#[error("Request already paid")]
|
||||
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
|
||||
#[error("Invalid payment request")]
|
||||
InvalidPaymentRequest,
|
||||
/// 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,
|
||||
/// 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,
|
||||
/// 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)]
|
||||
Secp256k1(#[from] bitcoin::secp256k1::Error),
|
||||
/// Secret error
|
||||
#[error(transparent)]
|
||||
Secret(#[from] super::secret::Error),
|
||||
Invoice(#[from] lightning_invoice::ParseOrSemanticError),
|
||||
/// Bip32 error
|
||||
#[error(transparent)]
|
||||
Bip32(#[from] bitcoin::bip32::Error),
|
||||
@@ -77,7 +167,7 @@ pub enum Error {
|
||||
UrlParseError(#[from] url::ParseError),
|
||||
/// Utf8 parse error
|
||||
#[error(transparent)]
|
||||
Utf8ParseError(#[from] FromUtf8Error),
|
||||
Utf8ParseError(#[from] std::string::FromUtf8Error),
|
||||
/// Serde Json error
|
||||
#[error(transparent)]
|
||||
SerdeJsonError(#[from] serde_json::Error),
|
||||
@@ -91,18 +181,51 @@ pub enum Error {
|
||||
/// From hex error
|
||||
#[error(transparent)]
|
||||
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
|
||||
#[error(transparent)]
|
||||
NUT01(#[from] crate::nuts::nut01::Error),
|
||||
/// NUT02 error
|
||||
#[error(transparent)]
|
||||
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
|
||||
#[error(transparent)]
|
||||
NUT11(#[from] crate::nuts::nut11::Error),
|
||||
/// Custom error
|
||||
#[error("`{0}`")]
|
||||
CustomError(String),
|
||||
/// NUT12 Error
|
||||
#[error(transparent)]
|
||||
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
|
||||
@@ -163,17 +286,7 @@ impl ErrorResponse {
|
||||
impl From<Error> for ErrorResponse {
|
||||
fn from(err: Error) -> ErrorResponse {
|
||||
match err {
|
||||
Error::TokenSpent => 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 {
|
||||
Error::UnitUnsupported => ErrorResponse {
|
||||
code: ErrorCode::UnitUnsupported,
|
||||
error: Some(err.to_string()),
|
||||
detail: None,
|
||||
@@ -188,6 +301,51 @@ impl From<Error> for ErrorResponse {
|
||||
error: Some("Invoice already paid.".to_string()),
|
||||
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 {
|
||||
code: ErrorCode::Unknown(9999),
|
||||
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
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub enum ErrorCode {
|
||||
@@ -204,14 +386,14 @@ pub enum ErrorCode {
|
||||
TokenAlreadySpent,
|
||||
/// Quote is not paid
|
||||
QuoteNotPaid,
|
||||
/// Quote is not expired
|
||||
QuoteExpired,
|
||||
/// Quote Pending
|
||||
QuotePending,
|
||||
/// Keyset is not found
|
||||
KeysetNotFound,
|
||||
/// Keyset inactive
|
||||
KeysetInactive,
|
||||
/// Fee Overpaid
|
||||
FeeOverPaid,
|
||||
/// Insufficient Fee
|
||||
InsufficientFee,
|
||||
/// Blinded Message Already signed
|
||||
BlindedMessageAlreadySigned,
|
||||
/// Unsupported unit
|
||||
@@ -220,8 +402,6 @@ pub enum ErrorCode {
|
||||
TokensAlreadyIssued,
|
||||
/// Minting Disabled
|
||||
MintingDisabled,
|
||||
/// Quote Pending
|
||||
QuotePending,
|
||||
/// Invoice Already Paid
|
||||
InvoiceAlreadyPaid,
|
||||
/// Token Not Verified
|
||||
@@ -230,6 +410,8 @@ pub enum ErrorCode {
|
||||
LightningError,
|
||||
/// Unbalanced Error
|
||||
TransactionUnbalanced,
|
||||
/// Amount outside of allowed range
|
||||
AmountOutofLimitRange,
|
||||
/// Unknown error code
|
||||
Unknown(u16),
|
||||
}
|
||||
@@ -243,8 +425,7 @@ impl ErrorCode {
|
||||
11001 => Self::TokenAlreadySpent,
|
||||
11002 => Self::TransactionUnbalanced,
|
||||
11005 => Self::UnitUnsupported,
|
||||
11006 => Self::InsufficientFee,
|
||||
11007 => Self::FeeOverPaid,
|
||||
11006 => Self::AmountOutofLimitRange,
|
||||
12001 => Self::KeysetNotFound,
|
||||
12002 => Self::KeysetInactive,
|
||||
20000 => Self::LightningError,
|
||||
@@ -253,6 +434,7 @@ impl ErrorCode {
|
||||
20003 => Self::MintingDisabled,
|
||||
20005 => Self::QuotePending,
|
||||
20006 => Self::InvoiceAlreadyPaid,
|
||||
20007 => Self::QuoteExpired,
|
||||
_ => Self::Unknown(code),
|
||||
}
|
||||
}
|
||||
@@ -265,8 +447,7 @@ impl ErrorCode {
|
||||
Self::TokenAlreadySpent => 11001,
|
||||
Self::TransactionUnbalanced => 11002,
|
||||
Self::UnitUnsupported => 11005,
|
||||
Self::InsufficientFee => 11006,
|
||||
Self::FeeOverPaid => 11007,
|
||||
Self::AmountOutofLimitRange => 11006,
|
||||
Self::KeysetNotFound => 12001,
|
||||
Self::KeysetInactive => 12002,
|
||||
Self::LightningError => 20000,
|
||||
@@ -275,6 +456,7 @@ impl ErrorCode {
|
||||
Self::MintingDisabled => 20003,
|
||||
Self::QuotePending => 20005,
|
||||
Self::InvoiceAlreadyPaid => 20006,
|
||||
Self::QuoteExpired => 20007,
|
||||
Self::Unknown(code) => *code,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ pub mod wallet;
|
||||
#[doc(hidden)]
|
||||
pub use bitcoin::secp256k1;
|
||||
#[doc(hidden)]
|
||||
pub use error::Error;
|
||||
#[doc(hidden)]
|
||||
pub use lightning_invoice::{self, Bolt11Invoice};
|
||||
#[cfg(feature = "mint")]
|
||||
#[doc(hidden)]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ use std::sync::Arc;
|
||||
|
||||
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
|
||||
use bitcoin::secp256k1::{self, Secp256k1};
|
||||
use error::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::instrument;
|
||||
@@ -15,13 +14,13 @@ use self::nut05::QuoteState;
|
||||
use self::nut11::EnforceSigFlag;
|
||||
use crate::cdk_database::{self, MintDatabase};
|
||||
use crate::dhke::{hash_to_curve, sign_message, verify_message};
|
||||
use crate::error::Error;
|
||||
use crate::mint_url::MintUrl;
|
||||
use crate::nuts::nut11::enforce_sig_flag;
|
||||
use crate::nuts::*;
|
||||
use crate::util::unix_time;
|
||||
use crate::Amount;
|
||||
|
||||
pub mod error;
|
||||
pub mod types;
|
||||
|
||||
pub use types::{MeltQuote, MintQuote};
|
||||
@@ -212,18 +211,26 @@ impl Mint {
|
||||
.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
|
||||
.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 => {
|
||||
return Err(Error::UnsupportedUnit);
|
||||
return Err(Error::UnitUnsupported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,18 +366,26 @@ impl Mint {
|
||||
.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
|
||||
.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 => {
|
||||
return Err(Error::UnsupportedUnit);
|
||||
return Err(Error::UnitUnsupported);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,6 +570,18 @@ impl Mint {
|
||||
&self,
|
||||
mint_request: nut04::MintBolt11Request,
|
||||
) -> 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
|
||||
.localstore
|
||||
.update_mint_quote_state(&mint_request.quote, MintQuoteState::Pending)
|
||||
|
||||
@@ -59,9 +59,18 @@ pub enum Error {
|
||||
/// Ciborium error
|
||||
#[error(transparent)]
|
||||
CiboriumError(#[from] ciborium::de::Error<std::io::Error>),
|
||||
/// CDK error
|
||||
/// Amount Error
|
||||
#[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
|
||||
#[error(transparent)]
|
||||
NUT11(#[from] crate::nuts::nut11::Error),
|
||||
|
||||
@@ -3,9 +3,21 @@
|
||||
//! <https://github.com/cashubtc/nuts/blob/main/03.md>
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
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
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
@@ -37,12 +49,16 @@ impl SwapRequest {
|
||||
|
||||
/// Total value of proofs in [`SwapRequest`]
|
||||
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`]
|
||||
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
|
||||
pub fn promises_amount(&self) -> Result<Amount, Error> {
|
||||
Amount::try_sum(
|
||||
Ok(Amount::try_sum(
|
||||
self.signatures
|
||||
.iter()
|
||||
.map(|BlindSignature { amount, .. }| *amount),
|
||||
)
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,18 @@ use std::str::FromStr;
|
||||
|
||||
use serde::ser::SerializeTuple;
|
||||
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
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
|
||||
@@ -26,9 +26,9 @@ pub enum Error {
|
||||
/// Invalid DLEQ Proof
|
||||
#[error("Invalid DLEQ proof")]
|
||||
InvalidDleqProof,
|
||||
/// Cashu Error
|
||||
/// DHKE error
|
||||
#[error(transparent)]
|
||||
Cashu(#[from] crate::error::Error),
|
||||
DHKE(#[from] crate::dhke::Error),
|
||||
/// NUT01 Error
|
||||
#[error(transparent)]
|
||||
NUT01(#[from] crate::nuts::nut01::Error),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//! <https://github.com/cashubtc/nuts/blob/main/13.md>
|
||||
|
||||
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
|
||||
use thiserror::Error;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::nut00::{BlindedMessage, PreMint, PreMintSecrets};
|
||||
@@ -10,11 +11,30 @@ use super::nut01::SecretKey;
|
||||
use super::nut02::Id;
|
||||
use crate::amount::SplitTarget;
|
||||
use crate::dhke::blind_message;
|
||||
use crate::error::Error;
|
||||
use crate::secret::Secret;
|
||||
use crate::util::hex;
|
||||
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 {
|
||||
/// Create new [`Secret`] from xpriv
|
||||
pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {
|
||||
|
||||
@@ -23,6 +23,9 @@ pub enum Error {
|
||||
/// Hex Error
|
||||
#[error(transparent)]
|
||||
Hex(#[from] hex::Error),
|
||||
/// Serde Json error
|
||||
#[error(transparent)]
|
||||
SerdeJsonError(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
impl Default for Secret {
|
||||
@@ -119,10 +122,10 @@ 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> {
|
||||
serde_json::from_str(&unchecked_secret.0)
|
||||
Ok(serde_json::from_str(&unchecked_secret.0)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,9 +44,7 @@ impl ProofInfo {
|
||||
state: State,
|
||||
unit: CurrencyUnit,
|
||||
) -> Result<Self, Error> {
|
||||
let y = proof
|
||||
.y()
|
||||
.map_err(|_| Error::CustomError("Could not find y".to_string()))?;
|
||||
let y = proof.y()?;
|
||||
|
||||
let spending_condition: Option<SpendingConditions> = (&proof.secret).try_into().ok();
|
||||
|
||||
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,12 @@ use bitcoin::hashes::sha256::Hash as Sha256Hash;
|
||||
use bitcoin::hashes::Hash;
|
||||
use bitcoin::key::XOnlyPublicKey;
|
||||
use bitcoin::Network;
|
||||
use error::Error;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::amount::SplitTarget;
|
||||
use crate::cdk_database::{self, WalletDatabase};
|
||||
use crate::dhke::{construct_proofs, hash_to_curve};
|
||||
use crate::error::Error;
|
||||
use crate::mint_url::MintUrl;
|
||||
use crate::nuts::nut00::token::Token;
|
||||
use crate::nuts::{
|
||||
@@ -28,7 +28,6 @@ use crate::util::{hex, unix_time};
|
||||
use crate::{Amount, Bolt11Invoice, HttpClient, SECP256K1};
|
||||
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod multi_mint_wallet;
|
||||
pub mod types;
|
||||
pub mod util;
|
||||
@@ -108,7 +107,7 @@ impl Wallet {
|
||||
.localstore
|
||||
.get_keyset_by_id(&proof.keyset_id)
|
||||
.await?
|
||||
.ok_or(Error::UnknownKey)?;
|
||||
.ok_or(Error::UnknownKeySet)?;
|
||||
|
||||
sum_fee += input_fee_ppk.input_fee_ppk;
|
||||
}
|
||||
@@ -125,7 +124,7 @@ impl Wallet {
|
||||
.localstore
|
||||
.get_keyset_by_id(keyset_id)
|
||||
.await?
|
||||
.ok_or(Error::UnknownKey)?
|
||||
.ok_or(Error::UnknownKeySet)?
|
||||
.input_fee_ppk;
|
||||
|
||||
let fee = (input_fee_ppk * count + 999) / 1000;
|
||||
@@ -615,12 +614,12 @@ impl Wallet {
|
||||
|
||||
let quote_info = if let Some(quote) = quote_info {
|
||||
if quote.expiry.le(&unix_time()) && quote.expiry.ne(&0) {
|
||||
return Err(Error::QuoteExpired);
|
||||
return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
|
||||
}
|
||||
|
||||
quote.clone()
|
||||
} else {
|
||||
return Err(Error::QuoteUnknown);
|
||||
return Err(Error::UnknownQuote);
|
||||
};
|
||||
|
||||
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) {
|
||||
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) {
|
||||
Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
|
||||
Err(_) => return Err(Error::CouldNotVerifyDleq),
|
||||
@@ -1292,7 +1291,7 @@ impl Wallet {
|
||||
let amount = match self.unit {
|
||||
CurrencyUnit::Sat => Amount::from(request_amount / 1000),
|
||||
CurrencyUnit::Msat => Amount::from(request_amount),
|
||||
_ => return Err(Error::UnitNotSupported),
|
||||
_ => return Err(Error::UnitUnsupported),
|
||||
};
|
||||
|
||||
let quote_res = self
|
||||
@@ -1352,12 +1351,12 @@ impl Wallet {
|
||||
let quote_info = self.localstore.get_melt_quote(quote_id).await?;
|
||||
let quote_info = if let Some(quote) = quote_info {
|
||||
if quote.expiry.le(&unix_time()) {
|
||||
return Err(Error::QuoteExpired);
|
||||
return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
|
||||
}
|
||||
|
||||
quote.clone()
|
||||
} else {
|
||||
return Err(Error::QuoteUnknown);
|
||||
return Err(Error::UnknownQuote);
|
||||
};
|
||||
|
||||
let ys = proofs
|
||||
@@ -1503,12 +1502,12 @@ impl Wallet {
|
||||
|
||||
let quote_info = if let Some(quote) = quote_info {
|
||||
if quote.expiry.le(&unix_time()) {
|
||||
return Err(Error::QuoteExpired);
|
||||
return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
|
||||
}
|
||||
|
||||
quote.clone()
|
||||
} else {
|
||||
return Err(Error::QuoteUnknown);
|
||||
return Err(Error::UnknownQuote);
|
||||
};
|
||||
|
||||
let inputs_needed_amount = quote_info.amount + quote_info.fee_reserve;
|
||||
@@ -1684,7 +1683,7 @@ impl Wallet {
|
||||
// Verify that proof DLEQ is valid
|
||||
if proof.dleq.is_some() {
|
||||
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)?;
|
||||
}
|
||||
|
||||
@@ -1815,7 +1814,7 @@ impl Wallet {
|
||||
let unit = token_data.unit().unwrap_or_default();
|
||||
|
||||
if unit != self.unit {
|
||||
return Err(Error::UnitNotSupported);
|
||||
return Err(Error::UnitUnsupported);
|
||||
}
|
||||
|
||||
let proofs = token_data.proofs();
|
||||
@@ -2108,7 +2107,7 @@ impl Wallet {
|
||||
key
|
||||
}
|
||||
}
|
||||
.ok_or(Error::UnknownKey)?;
|
||||
.ok_or(Error::AmountKey)?;
|
||||
|
||||
proof
|
||||
.verify_dleq(mint_pubkey)
|
||||
|
||||
5
justfile
5
justfile
@@ -7,7 +7,7 @@ alias t := test
|
||||
default:
|
||||
@just --list
|
||||
|
||||
final-check: format clippy test
|
||||
final-check: typos format clippy test
|
||||
|
||||
# run `cargo build` on everything
|
||||
build *ARGS="--workspace --all-targets":
|
||||
@@ -54,6 +54,9 @@ clippy *ARGS="--locked --offline --workspace --all-targets":
|
||||
clippy-fix *ARGS="--locked --offline --workspace --all-targets":
|
||||
cargo clippy {{ARGS}} --fix
|
||||
|
||||
typos:
|
||||
typos
|
||||
|
||||
# fix all typos
|
||||
[no-exit-message]
|
||||
typos-fix:
|
||||
|
||||
Reference in New Issue
Block a user