refactor: v1 MeltRequest

TODO: ffi bindings
This commit is contained in:
thesimplekid
2023-12-11 22:35:47 +00:00
parent 35db3fb147
commit c706e367e9
22 changed files with 458 additions and 498 deletions

View File

@@ -4,13 +4,12 @@ use async_trait::async_trait;
#[cfg(feature = "nut09")]
use cashu::nuts::MintInfo;
use cashu::nuts::{
BlindedMessage, CheckFeesRequest, CheckFeesResponse, Keys, MeltRequest, MeltResponse,
MintRequest, PostMintResponse, PreMintSecrets, Proof, RequestMintResponse, SplitRequest,
SplitResponse, *,
BlindedMessage, Keys, MeltBolt11Request, MeltBolt11Response, MintRequest, PostMintResponse,
PreMintSecrets, Proof, RequestMintResponse, SplitRequest, SplitResponse, *,
};
#[cfg(feature = "nut07")]
use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
use cashu::{Amount, Bolt11Invoice};
use cashu::Amount;
use gloo::net::http::Request;
use serde_json::Value;
use url::Url;
@@ -118,49 +117,20 @@ impl Client for HttpClient {
}
}
/// Check Max expected fee [NUT-05]
async fn post_check_fees(
&self,
mint_url: Url,
invoice: Bolt11Invoice,
) -> Result<CheckFeesResponse, Error> {
let url = join_url(mint_url, "checkfees")?;
let request = CheckFeesRequest { pr: invoice };
let res = Request::post(url.as_str())
.json(&request)
.map_err(|err| Error::Gloo(err.to_string()))?
.send()
.await
.map_err(|err| Error::Gloo(err.to_string()))?
.json::<Value>()
.await
.map_err(|err| Error::Gloo(err.to_string()))?;
let response: Result<CheckFeesResponse, serde_json::Error> =
serde_json::from_value(res.clone());
match response {
Ok(res) => Ok(res),
Err(_) => Err(Error::from_json(&res.to_string())?),
}
}
/// Melt [NUT-05]
/// [Nut-08] Lightning fee return if outputs defined
async fn post_melt(
&self,
mint_url: Url,
proofs: Vec<Proof>,
invoice: Bolt11Invoice,
quote: String,
inputs: Vec<Proof>,
outputs: Option<Vec<BlindedMessage>>,
) -> Result<MeltResponse, Error> {
) -> Result<MeltBolt11Response, Error> {
let url = join_url(mint_url, "melt")?;
let request = MeltRequest {
proofs,
pr: invoice,
let request = MeltBolt11Request {
quote,
inputs,
outputs,
};
@@ -174,7 +144,7 @@ impl Client for HttpClient {
.await
.map_err(|err| Error::Gloo(err.to_string()))?;
let response: Result<MeltResponse, serde_json::Error> =
let response: Result<MeltBolt11Response, serde_json::Error> =
serde_json::from_value(value.clone());
match response {

View File

@@ -6,13 +6,12 @@ use async_trait::async_trait;
#[cfg(feature = "nut09")]
use cashu::nuts::MintInfo;
use cashu::nuts::{
BlindedMessage, CheckFeesRequest, CheckFeesResponse, Keys, MeltRequest, MeltResponse,
MintRequest, PostMintResponse, PreMintSecrets, Proof, RequestMintResponse, SplitRequest,
SplitResponse, *,
BlindedMessage, Keys, MeltBolt11Request, MeltBolt11Response, MintRequest, PostMintResponse,
PreMintSecrets, Proof, RequestMintResponse, SplitRequest, SplitResponse, *,
};
#[cfg(feature = "nut07")]
use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
use cashu::{Amount, Bolt11Invoice};
use cashu::Amount;
use serde_json::Value;
use url::Url;
@@ -96,44 +95,20 @@ impl Client for HttpClient {
}
}
/// Check Max expected fee [NUT-05]
async fn post_check_fees(
&self,
mint_url: Url,
invoice: Bolt11Invoice,
) -> Result<CheckFeesResponse, Error> {
let url = join_url(mint_url, "checkfees")?;
let request = CheckFeesRequest { pr: invoice };
let res = minreq::post(url)
.with_json(&request)?
.send()?
.json::<Value>()?;
let response: Result<CheckFeesResponse, serde_json::Error> =
serde_json::from_value(res.clone());
match response {
Ok(res) => Ok(res),
Err(_) => Err(Error::from_json(&res.to_string())?),
}
}
/// Melt [NUT-05]
/// [Nut-08] Lightning fee return if outputs defined
async fn post_melt(
&self,
mint_url: Url,
proofs: Vec<Proof>,
invoice: Bolt11Invoice,
quote: String,
inputs: Vec<Proof>,
outputs: Option<Vec<BlindedMessage>>,
) -> Result<MeltResponse, Error> {
) -> Result<MeltBolt11Response, Error> {
let url = join_url(mint_url, "melt")?;
let request = MeltRequest {
proofs,
pr: invoice,
let request = MeltBolt11Request {
quote,
inputs,
outputs,
};
@@ -142,7 +117,7 @@ impl Client for HttpClient {
.send()?
.json::<Value>()?;
let response: Result<MeltResponse, serde_json::Error> =
let response: Result<MeltBolt11Response, serde_json::Error> =
serde_json::from_value(value.clone());
match response {

View File

@@ -8,8 +8,8 @@ use cashu::nuts::CheckSpendableResponse;
#[cfg(feature = "nut09")]
use cashu::nuts::MintInfo;
use cashu::nuts::{
BlindedMessage, CheckFeesResponse, Keys, KeysetResponse, MeltResponse, PostMintResponse,
PreMintSecrets, Proof, RequestMintResponse, SplitRequest, SplitResponse,
BlindedMessage, Keys, KeysetResponse, MeltBolt11Response, PostMintResponse, PreMintSecrets,
Proof, RequestMintResponse, SplitRequest, SplitResponse,
};
use cashu::{utils, Amount};
use serde::{Deserialize, Serialize};
@@ -103,19 +103,13 @@ pub trait Client {
hash: &str,
) -> Result<PostMintResponse, Error>;
async fn post_check_fees(
&self,
mint_url: Url,
invoice: Bolt11Invoice,
) -> Result<CheckFeesResponse, Error>;
async fn post_melt(
&self,
mint_url: Url,
proofs: Vec<Proof>,
invoice: Bolt11Invoice,
quote: String,
inputs: Vec<Proof>,
outputs: Option<Vec<BlindedMessage>>,
) -> Result<MeltResponse, Error>;
) -> Result<MeltBolt11Response, Error>;
// REVIEW: Should be consistent aboue passing in the Request struct or the
// compnatants and making it within the function. Here the struct is passed

View File

@@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
use cashu::dhke::{sign_message, verify_message};
pub use cashu::error::mint::Error;
use cashu::nuts::{
BlindedMessage, BlindedSignature, MeltRequest, MeltResponse, Proof, SplitRequest,
BlindedMessage, BlindedSignature, MeltBolt11Request, MeltBolt11Response, Proof, SplitRequest,
SplitResponse, *,
};
#[cfg(feature = "nut07")]
@@ -13,6 +13,8 @@ use cashu::Amount;
use serde::{Deserialize, Serialize};
use tracing::{debug, info};
use crate::types::Quote;
pub struct Mint {
// pub pubkey: PublicKey
secret: String,
@@ -21,6 +23,7 @@ pub struct Mint {
pub spent_secrets: HashSet<Secret>,
pub pending_secrets: HashSet<Secret>,
pub fee_reserve: FeeReserve,
pub quotes: HashMap<String, Quote>,
}
impl Mint {
@@ -28,6 +31,7 @@ impl Mint {
secret: &str,
keysets_info: HashSet<MintKeySetInfo>,
spent_secrets: HashSet<Secret>,
quotes: Vec<Quote>,
min_fee_reserve: Amount,
percent_fee_reserve: f32,
) -> Self {
@@ -36,6 +40,8 @@ impl Mint {
let mut active_units: HashSet<String> = HashSet::default();
let quotes = quotes.into_iter().map(|q| (q.id.clone(), q)).collect();
// Check that there is only one active keyset per unit
for keyset_info in keysets_info {
if keyset_info.active && !active_units.insert(keyset_info.unit.clone()) {
@@ -58,6 +64,7 @@ impl Mint {
Self {
secret: secret.to_string(),
keysets,
quotes,
keysets_info: info,
spent_secrets,
pending_secrets: HashSet::new(),
@@ -222,41 +229,28 @@ impl Mint {
Ok(CheckSpendableResponse { spendable, pending })
}
pub fn verify_melt_request(&mut self, melt_request: &MeltRequest) -> Result<(), Error> {
let proofs_total = melt_request.proofs_amount();
pub fn verify_melt_request(&mut self, melt_request: &MeltBolt11Request) -> Result<(), Error> {
let quote = self.quotes.get(&melt_request.quote).unwrap();
let proofs_total = melt_request.proofs_amount().to_sat();
let percent_fee_reserve = Amount::from_sat(
(proofs_total.to_sat() as f32 * self.fee_reserve.percent_fee_reserve) as u64,
);
let fee_reserve = if percent_fee_reserve > self.fee_reserve.min_fee_reserve {
percent_fee_reserve
} else {
self.fee_reserve.min_fee_reserve
};
let required_total = melt_request
.invoice_amount()
.map_err(|_| Error::InvoiceAmountUndefined)?
+ fee_reserve;
let required_total = quote.amount + quote.fee_reserve;
if proofs_total < required_total {
debug!(
"Insufficient Proofs: Got: {}, Required: {}",
proofs_total.to_sat().to_string(),
required_total.to_sat().to_string()
proofs_total, required_total
);
return Err(Error::Amount);
}
let secrets: HashSet<&Secret> = melt_request.proofs.iter().map(|p| &p.secret).collect();
let secrets: HashSet<&Secret> = melt_request.inputs.iter().map(|p| &p.secret).collect();
// Ensure proofs are unique and not being double spent
if melt_request.proofs.len().ne(&secrets.len()) {
if melt_request.inputs.len().ne(&secrets.len()) {
return Err(Error::DuplicateProofs);
}
for proof in &melt_request.proofs {
for proof in &melt_request.inputs {
self.verify_proof(proof)?
}
@@ -265,13 +259,13 @@ impl Mint {
pub fn process_melt_request(
&mut self,
melt_request: &MeltRequest,
melt_request: &MeltBolt11Request,
preimage: &str,
total_spent: Amount,
) -> Result<MeltResponse, Error> {
) -> Result<MeltBolt11Response, Error> {
self.verify_melt_request(melt_request)?;
let secrets = Vec::with_capacity(melt_request.proofs.len());
let secrets = Vec::with_capacity(melt_request.inputs.len());
for secret in secrets {
self.spent_secrets.insert(secret);
}
@@ -312,9 +306,9 @@ impl Mint {
);
}
Ok(MeltResponse {
Ok(MeltBolt11Response {
paid: true,
preimage: Some(preimage.to_string()),
proof: preimage.to_string(),
change,
})
}

View File

@@ -5,8 +5,8 @@ use cashu::dhke::{construct_proofs, unblind_message};
#[cfg(feature = "nut07")]
use cashu::nuts::nut00::mint;
use cashu::nuts::{
BlindedSignature, Keys, PreMintSecrets, PreSplit, Proof, Proofs, RequestMintResponse,
SplitRequest, Token,
BlindedSignature, CurrencyUnit, Keys, PreMintSecrets, PreSplit, Proof, Proofs,
RequestMintResponse, SplitRequest, Token,
};
#[cfg(feature = "nut07")]
use cashu::types::ProofsStatus;
@@ -95,12 +95,12 @@ impl<C: Client> Wallet<C> {
&self,
amount: Amount,
hash: &str,
unit: Option<String>,
memo: Option<String>,
unit: Option<CurrencyUnit>,
) -> Result<Token, Error> {
let proofs = self.mint(amount, hash).await?;
let token = Token::new(self.mint_url.clone(), proofs, unit, memo);
let token = Token::new(self.mint_url.clone(), proofs, memo, unit);
Ok(token?)
}
@@ -127,15 +127,6 @@ impl<C: Client> Wallet<C> {
Ok(proofs)
}
/// Check fee
pub async fn check_fee(&self, invoice: Bolt11Invoice) -> Result<Amount, Error> {
Ok(self
.client
.post_check_fees(self.mint_url.clone().try_into()?, invoice)
.await?
.fee)
}
/// Receive
pub async fn receive(&self, encoded_token: &str) -> Result<Proofs, Error> {
let token_data = Token::from_str(encoded_token)?;
@@ -300,7 +291,7 @@ impl<C: Client> Wallet<C> {
pub async fn melt(
&self,
invoice: Bolt11Invoice,
quote: String,
proofs: Proofs,
fee_reserve: Amount,
) -> Result<Melted, Error> {
@@ -309,8 +300,8 @@ impl<C: Client> Wallet<C> {
.client
.post_melt(
self.mint_url.clone().try_into()?,
quote,
proofs,
invoice,
Some(blinded.blinded_messages()),
)
.await?;
@@ -327,7 +318,7 @@ impl<C: Client> Wallet<C> {
let melted = Melted {
paid: true,
preimage: melt_response.preimage,
preimage: Some(melt_response.proof),
change: change_proofs,
};
@@ -337,10 +328,10 @@ impl<C: Client> Wallet<C> {
pub fn proofs_to_token(
&self,
proofs: Proofs,
unit: Option<String>,
memo: Option<String>,
unit: Option<CurrencyUnit>,
) -> Result<String, Error> {
Ok(Token::new(self.mint_url.clone(), proofs, unit, memo)?.to_string())
Ok(Token::new(self.mint_url.clone(), proofs, memo, unit)?.to_string())
}
}