mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-05 05:06:14 +01:00
feat(nut07): check state on y
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)?
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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())?)
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user