feat(nut07): check state on y

This commit is contained in:
thesimplekid
2024-03-22 15:01:48 +00:00
parent 4cab82d15a
commit 7076c4db5f
9 changed files with 46 additions and 75 deletions

View File

@@ -6,7 +6,7 @@ use cashu::nuts::{
MintInfo, PreMintSecrets, Proof, SwapRequest, SwapResponse, *,
};
#[cfg(feature = "nut07")]
use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse};
use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse, PublicKey};
use cashu::{Amount, Bolt11Invoice};
use gloo::net::http::Request;
use serde_json::Value;
@@ -212,10 +212,10 @@ impl Client for HttpClient {
async fn post_check_state(
&self,
mint_url: Url,
secrets: Vec<Secret>,
ys: Vec<PublicKey>,
) -> Result<CheckStateResponse, Error> {
let url = join_url(mint_url, &["v1", "check"])?;
let request = CheckSpendableRequest { secrets };
let request = CheckSpendableRequest { ys };
let res = Request::post(url.as_str())
.json(&request)

View File

@@ -2,6 +2,8 @@
use async_trait::async_trait;
use cashu::error::ErrorResponse;
#[cfg(feature = "nut07")]
use cashu::nuts::PublicKey;
use cashu::nuts::{
BlindedMessage, CurrencyUnit, Id, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request,
MeltBolt11Response, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request,
@@ -10,8 +12,6 @@ use cashu::nuts::{
};
#[cfg(feature = "nut07")]
use cashu::nuts::{CheckStateRequest, CheckStateResponse};
#[cfg(feature = "nut07")]
use cashu::secret::Secret;
use cashu::{Amount, Bolt11Invoice};
use serde_json::Value;
use tracing::warn;
@@ -189,10 +189,10 @@ impl Client for HttpClient {
async fn post_check_state(
&self,
mint_url: Url,
secrets: Vec<Secret>,
ys: Vec<PublicKey>,
) -> Result<CheckStateResponse, Error> {
let url = join_url(mint_url, &["v1", "checkstate"])?;
let request = CheckStateRequest { secrets };
let request = CheckStateRequest { ys };
let res = minreq::post(url)
.with_json(&request)?

View File

@@ -4,13 +4,13 @@ use async_trait::async_trait;
use cashu::error::ErrorResponse;
#[cfg(feature = "nut07")]
use cashu::nuts::CheckStateResponse;
#[cfg(feature = "nut07")]
use cashu::nuts::PublicKey;
use cashu::nuts::{
BlindedMessage, CurrencyUnit, Id, KeySet, KeysetResponse, MeltBolt11Response,
MeltQuoteBolt11Response, MintBolt11Response, MintInfo, MintQuoteBolt11Response, PreMintSecrets,
Proof, SwapRequest, SwapResponse,
};
#[cfg(feature = "nut07")]
use cashu::secret::Secret;
use cashu::Amount;
use thiserror::Error;
use url::Url;
@@ -107,7 +107,7 @@ pub trait Client {
async fn post_check_state(
&self,
mint_url: Url,
secrets: Vec<Secret>,
ys: Vec<PublicKey>,
) -> Result<CheckStateResponse, Error>;
async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error>;

View File

@@ -3,9 +3,8 @@ use std::sync::Arc;
use async_trait::async_trait;
use cashu::dhke::hash_to_curve;
use cashu::k256;
use cashu::nuts::nut02::mint::KeySet;
use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof, Proofs};
use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof, Proofs, PublicKey};
use cashu::secret::Secret;
use cashu::types::{MeltQuote, MintQuote};
use tokio::sync::Mutex;
@@ -173,15 +172,12 @@ impl LocalStore for MemoryLocalStore {
.cloned())
}
async fn get_spent_proof_by_hash(
&self,
secret: &k256::PublicKey,
) -> Result<Option<Proof>, Error> {
async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error> {
Ok(self
.spent_proofs
.lock()
.await
.get(&secret.to_sec1_bytes().to_vec())
.get(&y.to_bytes().to_vec())
.cloned())
}
@@ -205,15 +201,12 @@ impl LocalStore for MemoryLocalStore {
.cloned())
}
async fn get_pending_proof_by_hash(
&self,
secret: &k256::PublicKey,
) -> Result<Option<Proof>, Error> {
async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error> {
Ok(self
.pending_proofs
.lock()
.await
.get(&secret.to_sec1_bytes().to_vec())
.get(&y.to_bytes().to_vec())
.cloned())
}

View File

@@ -5,9 +5,8 @@ pub mod redb_store;
use std::collections::HashMap;
use async_trait::async_trait;
use cashu::k256;
use cashu::nuts::nut02::mint::KeySet;
use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof};
use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof, PublicKey};
use cashu::secret::Secret;
use cashu::types::{MeltQuote, MintQuote};
pub use memory::MemoryLocalStore;
@@ -73,16 +72,10 @@ pub trait LocalStore {
async fn add_spent_proof(&self, proof: Proof) -> Result<(), Error>;
async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result<Option<Proof>, Error>;
async fn get_spent_proof_by_hash(
&self,
secret: &k256::PublicKey,
) -> Result<Option<Proof>, Error>;
async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error>;
async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error>;
async fn get_pending_proof_by_secret(&self, secret: &Secret) -> Result<Option<Proof>, Error>;
async fn get_pending_proof_by_hash(
&self,
secret: &k256::PublicKey,
) -> Result<Option<Proof>, Error>;
async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error>;
async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Error>;
}

View File

@@ -4,8 +4,7 @@ use std::sync::Arc;
use async_trait::async_trait;
use cashu::dhke::hash_to_curve;
use cashu::k256;
use cashu::nuts::{CurrencyUnit, Id, MintInfo, MintKeySet as KeySet, Proof};
use cashu::nuts::{CurrencyUnit, Id, MintInfo, MintKeySet as KeySet, Proof, PublicKey};
use cashu::secret::Secret;
use cashu::types::{MeltQuote, MintQuote};
use redb::{Database, ReadableTable, TableDefinition};
@@ -297,15 +296,12 @@ impl LocalStore for RedbLocalStore {
Ok(())
}
async fn get_spent_proof_by_hash(
&self,
secret_point: &k256::PublicKey,
) -> Result<Option<Proof>, Error> {
async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error> {
let db = self.db.lock().await;
let read_txn = db.begin_read()?;
let table = read_txn.open_table(SPENT_PROOFS_TABLE)?;
let proof = table.get(secret_point.to_sec1_bytes().as_ref())?;
let proof = table.get(y.to_bytes().as_ref())?;
if let Some(proof) = proof {
Ok(serde_json::from_str(proof.value())?)
@@ -351,15 +347,12 @@ impl LocalStore for RedbLocalStore {
Ok(())
}
async fn get_pending_proof_by_hash(
&self,
secret_point: &k256::PublicKey,
) -> Result<Option<Proof>, Error> {
async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error> {
let db = self.db.lock().await;
let read_txn = db.begin_read()?;
let table = read_txn.open_table(PENDING_PROOFS_TABLE)?;
let proof = table.get(secret_point.to_sec1_bytes().as_ref())?;
let proof = table.get(y.to_bytes().as_ref())?;
if let Some(proof) = proof {
Ok(serde_json::from_str(proof.value())?)

View File

@@ -473,18 +473,13 @@ impl Mint {
}
}
let y = hash_to_curve(&proof.secret.to_bytes())?;
let y: PublicKey = hash_to_curve(&proof.secret.to_bytes())?.into();
if self.localstore.get_spent_proof_by_hash(&y).await?.is_some() {
if self.localstore.get_spent_proof_by_y(&y).await?.is_some() {
return Err(Error::TokenSpent);
}
if self
.localstore
.get_pending_proof_by_hash(&y)
.await?
.is_some()
{
if self.localstore.get_pending_proof_by_y(&y).await?.is_some() {
return Err(Error::TokenPending);
}
@@ -512,29 +507,19 @@ impl Mint {
&self,
check_state: &CheckStateRequest,
) -> Result<CheckStateResponse, Error> {
let mut states = Vec::with_capacity(check_state.secrets.len());
let mut states = Vec::with_capacity(check_state.ys.len());
for secret in &check_state.secrets {
let state = if self
.localstore
.get_spent_proof_by_secret(secret)
.await?
.is_some()
{
for y in &check_state.ys {
let state = if self.localstore.get_spent_proof_by_y(y).await?.is_some() {
State::Spent
} else if self
.localstore
.get_pending_proof_by_secret(secret)
.await?
.is_some()
{
} else if self.localstore.get_pending_proof_by_y(y).await?.is_some() {
State::Pending
} else {
State::Unspent
};
states.push(ProofState {
secret: secret.clone(),
y: y.clone(),
state,
witness: None,
})

View File

@@ -7,12 +7,12 @@ use cashu::dhke::{construct_proofs, unblind_message};
#[cfg(feature = "nut07")]
use cashu::nuts::nut07::ProofState;
use cashu::nuts::nut11::SigningKey;
#[cfg(feature = "nut07")]
use cashu::nuts::PublicKey;
use cashu::nuts::{
BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, P2PKConditions, PreMintSecrets,
PreSwap, Proof, Proofs, SigFlag, SwapRequest, Token,
};
#[cfg(feature = "nut07")]
use cashu::secret::Secret;
use cashu::types::{MeltQuote, Melted, MintQuote};
use cashu::url::UncheckedUrl;
use cashu::{Amount, Bolt11Invoice};
@@ -153,6 +153,8 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
mint_url: UncheckedUrl,
proofs: Proofs,
) -> Result<Vec<ProofState>, Error> {
use cashu::dhke::hash_to_curve;
let spendable = self
.client
.post_check_state(
@@ -160,8 +162,10 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
proofs
.clone()
.into_iter()
.map(|p| p.secret)
.collect::<Vec<Secret>>()
// Find Y for the secret
.flat_map(|p| hash_to_curve(&p.secret.to_bytes()))
.map(|y| y.into())
.collect::<Vec<PublicKey>>()
.clone(),
)
.await?;

View File

@@ -3,7 +3,7 @@
use serde::{Deserialize, Serialize};
use crate::secret::Secret;
use super::PublicKey;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
@@ -16,14 +16,17 @@ pub enum State {
/// Check spendabale request [NUT-07]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CheckStateRequest {
pub secrets: Vec<Secret>,
/// Y's of the proofs to check
#[serde(rename = "Ys")]
pub ys: Vec<PublicKey>,
}
/// Proof state [NUT-07]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProofState {
/// Secret of proof
pub secret: Secret,
/// Y of proof
#[serde(rename = "Y")]
pub y: PublicKey,
/// State of proof
pub state: State,
/// Witness data if it is supplied