refactor: nut-07 state

This commit is contained in:
thesimplekid
2024-01-06 14:49:49 +00:00
parent d0d3a6732f
commit f9f88edfd8
7 changed files with 85 additions and 64 deletions

View File

@@ -209,15 +209,13 @@ impl Client for HttpClient {
/// Spendable check [NUT-07]
#[cfg(feature = "nut07")]
async fn post_check_spendable(
async fn post_check_state(
&self,
mint_url: Url,
proofs: Vec<nut00::mint::Proof>,
) -> Result<CheckSpendableResponse, Error> {
secrets: Vec<Secret>,
) -> Result<CheckStateResponse, Error> {
let url = join_url(mint_url, &["v1", "check"])?;
let request = CheckSpendableRequest {
proofs: proofs.to_owned(),
};
let request = CheckSpendableRequest { secrets };
let res = Request::post(url.as_str())
.json(&request)

View File

@@ -4,13 +4,14 @@ use std::println;
use async_trait::async_trait;
use cashu::nuts::{
nut00, BlindedMessage, CurrencyUnit, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request,
BlindedMessage, CurrencyUnit, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request,
MeltBolt11Response, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request,
MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, PreMintSecrets,
Proof, SwapRequest, SwapResponse,
};
#[cfg(feature = "nut07")]
use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
use cashu::nuts::{CheckStateRequest, CheckStateResponse};
use cashu::secret::Secret;
use cashu::{Amount, Bolt11Invoice};
use serde_json::Value;
use tracing::warn;
@@ -179,20 +180,20 @@ impl Client for HttpClient {
/// Spendable check [NUT-07]
#[cfg(feature = "nut07")]
async fn post_check_spendable(
async fn post_check_state(
&self,
mint_url: Url,
proofs: Vec<nut00::mint::Proof>,
) -> Result<CheckSpendableResponse, Error> {
let url = join_url(mint_url, &["v1", "check"])?;
let request = CheckSpendableRequest { proofs };
secrets: Vec<Secret>,
) -> Result<CheckStateResponse, Error> {
let url = join_url(mint_url, &["v1", "checkstate"])?;
let request = CheckStateRequest { secrets };
let res = minreq::post(url)
.with_json(&request)?
.send()?
.json::<Value>()?;
let response: Result<CheckSpendableResponse, serde_json::Error> =
let response: Result<CheckStateResponse, serde_json::Error> =
serde_json::from_value(res.clone());
match response {

View File

@@ -1,15 +1,14 @@
//! Client to connet to mint
use async_trait::async_trait;
#[cfg(feature = "mint")]
use cashu::nuts::nut00;
#[cfg(feature = "nut07")]
use cashu::nuts::CheckSpendableResponse;
use cashu::nuts::CheckStateResponse;
use cashu::nuts::{
BlindedMessage, CurrencyUnit, KeySet, KeysetResponse, MeltBolt11Response,
MeltQuoteBolt11Response, MintBolt11Response, MintInfo, MintQuoteBolt11Response, PreMintSecrets,
Proof, SwapRequest, SwapResponse,
};
use cashu::secret::Secret;
use cashu::{utils, Amount};
use serde::{Deserialize, Serialize};
use thiserror::Error;
@@ -127,11 +126,11 @@ pub trait Client {
) -> Result<SwapResponse, Error>;
#[cfg(feature = "nut07")]
async fn post_check_spendable(
async fn post_check_state(
&self,
mint_url: Url,
proofs: Vec<nut00::mint::Proof>,
) -> Result<CheckSpendableResponse, Error>;
secrets: Vec<Secret>,
) -> Result<CheckStateResponse, Error>;
async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error>;
}

View File

@@ -1,12 +1,14 @@
use std::collections::HashSet;
use cashu::dhke::{sign_message, verify_message};
#[cfg(feature = "nut07")]
use cashu::nuts::nut07::State;
use cashu::nuts::{
BlindedMessage, BlindedSignature, MeltBolt11Request, MeltBolt11Response, Proof, SwapRequest,
SwapResponse, *,
};
#[cfg(feature = "nut07")]
use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
use cashu::nuts::{CheckStateRequest, CheckStateResponse};
use cashu::secret::Secret;
use cashu::types::{MeltQuote, MintQuote};
use cashu::Amount;
@@ -369,29 +371,41 @@ impl<L: LocalStore> Mint<L> {
#[cfg(feature = "nut07")]
pub async fn check_spendable(
&self,
check_spendable: &CheckSpendableRequest,
) -> Result<CheckSpendableResponse, Error> {
let mut spendable = Vec::with_capacity(check_spendable.proofs.len());
let mut pending = Vec::with_capacity(check_spendable.proofs.len());
check_spendable: &CheckStateRequest,
) -> Result<CheckStateResponse, Error> {
use cashu::nuts::nut07::ProofState;
for proof in &check_spendable.proofs {
spendable.push(
self.localstore
.get_spent_proof(&proof.secret)
.await
.unwrap()
.is_none(),
);
pending.push(
self.localstore
.get_pending_proof(&proof.secret)
.await
.unwrap()
.is_some(),
);
let mut states = Vec::with_capacity(check_spendable.secrets.len());
for secret in &check_spendable.secrets {
let state = if self
.localstore
.get_spent_proof(secret)
.await
.unwrap()
.is_some()
{
State::Spent
} else if self
.localstore
.get_pending_proof(secret)
.await
.unwrap()
.is_some()
{
State::Pending
} else {
State::Unspent
};
states.push(ProofState {
secret: secret.clone(),
state,
witness: None,
})
}
Ok(CheckSpendableResponse { spendable, pending })
Ok(CheckStateResponse { states })
}
pub async fn verify_melt_request(

View File

@@ -5,13 +5,13 @@ use std::str::FromStr;
use bip39::Mnemonic;
use cashu::dhke::{construct_proofs, unblind_message};
#[cfg(feature = "nut07")]
use cashu::nuts::nut00::mint;
use cashu::nuts::nut07::ProofState;
use cashu::nuts::{
BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, PreMintSecrets, PreSwap, Proof, Proofs,
SwapRequest, Token,
};
#[cfg(feature = "nut07")]
use cashu::types::ProofsStatus;
use cashu::secret::Secret;
use cashu::types::{MeltQuote, Melted, MintQuote};
use cashu::url::UncheckedUrl;
use cashu::{Amount, Bolt11Invoice};
@@ -125,30 +125,23 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
&self,
mint_url: UncheckedUrl,
proofs: Proofs,
) -> Result<ProofsStatus, Error> {
) -> Result<Vec<ProofState>, Error> {
let spendable = self
.client
.post_check_spendable(
.post_check_state(
mint_url.try_into()?,
proofs
.clone()
.into_iter()
.map(|p| p.into())
.collect::<mint::Proofs>()
.map(|p| p.secret)
.collect::<Vec<Secret>>()
.clone(),
)
.await?;
// Separate proofs in spent and unspent based on mint response
let (spendable, spent): (Vec<_>, Vec<_>) = proofs
.iter()
.zip(spendable.spendable.iter())
.partition(|(_, &b)| b);
Ok(ProofsStatus {
spendable: spendable.into_iter().map(|(s, _)| s).cloned().collect(),
spent: spent.into_iter().map(|(s, _)| s).cloned().collect(),
})
Ok(spendable.states)
}
/*

View File

@@ -28,7 +28,7 @@ pub use nut05::{MeltQuoteBolt11Request, MeltQuoteBolt11Response};
pub use nut06::{MintInfo, MintVersion, Nuts};
#[cfg(feature = "wallet")]
#[cfg(feature = "nut07")]
pub use nut07::{CheckSpendableRequest, CheckSpendableResponse};
pub use nut07::{CheckStateRequest, CheckStateResponse};
#[cfg(feature = "nut08")]
pub use nut08::{MeltBolt11Request, MeltBolt11Response};

View File

@@ -3,21 +3,37 @@
use serde::{Deserialize, Serialize};
use super::nut00::mint;
use crate::secret::Secret;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum State {
Spent,
Unspent,
Pending,
}
/// Check spendabale request [NUT-07]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CheckSpendableRequest {
pub proofs: mint::Proofs,
pub struct CheckStateRequest {
pub secrets: Vec<Secret>,
}
/// Proof state [NUT-07]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProofState {
/// Secret of proof
pub secret: Secret,
/// State of proof
pub state: State,
/// Witness data if it is supplied
pub witness: Option<String>,
}
/// Check Spendable Response [NUT-07]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CheckSpendableResponse {
/// booleans indicating whether the provided Proof is still spendable.
/// In same order as provided proofs
pub spendable: Vec<bool>,
pub pending: Vec<bool>,
pub struct CheckStateResponse {
pub states: Vec<ProofState>,
}
/// Spendable Settings