mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-09 15:16:00 +01:00
feat(mint): store blinded messages and sigs
This commit is contained in:
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use cashu::dhke::hash_to_curve;
|
||||
use cashu::nuts::nut02::mint::KeySet;
|
||||
use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof, Proofs, PublicKey};
|
||||
use cashu::nuts::{BlindedSignature, CurrencyUnit, Id, MintInfo, Proof, Proofs, PublicKey};
|
||||
use cashu::secret::Secret;
|
||||
use cashu::types::{MeltQuote, MintQuote};
|
||||
use tokio::sync::Mutex;
|
||||
@@ -20,9 +20,11 @@ pub struct MemoryLocalStore {
|
||||
melt_quotes: Arc<Mutex<HashMap<String, MeltQuote>>>,
|
||||
pending_proofs: Arc<Mutex<HashMap<Vec<u8>, Proof>>>,
|
||||
spent_proofs: Arc<Mutex<HashMap<Vec<u8>, Proof>>>,
|
||||
blinded_signatures: Arc<Mutex<HashMap<String, BlindedSignature>>>,
|
||||
}
|
||||
|
||||
impl MemoryLocalStore {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
mint_info: MintInfo,
|
||||
active_keysets: HashMap<CurrencyUnit, Id>,
|
||||
@@ -31,6 +33,7 @@ impl MemoryLocalStore {
|
||||
melt_quotes: Vec<MeltQuote>,
|
||||
pending_proofs: Proofs,
|
||||
spent_proofs: Proofs,
|
||||
blinded_signatures: HashMap<String, BlindedSignature>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
mint_info: Arc::new(Mutex::new(mint_info)),
|
||||
@@ -70,6 +73,7 @@ impl MemoryLocalStore {
|
||||
})
|
||||
.collect(),
|
||||
)),
|
||||
blinded_signatures: Arc::new(Mutex::new(blinded_signatures)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -218,4 +222,27 @@ impl LocalStore for MemoryLocalStore {
|
||||
.remove(&secret_point.to_sec1_bytes().to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_blinded_signature(
|
||||
&self,
|
||||
blinded_message: PublicKey,
|
||||
blinded_signature: BlindedSignature,
|
||||
) -> Result<(), Error> {
|
||||
self.blinded_signatures
|
||||
.lock()
|
||||
.await
|
||||
.insert(blinded_message.to_string(), blinded_signature);
|
||||
Ok(())
|
||||
}
|
||||
async fn get_blinded_signature(
|
||||
&self,
|
||||
blinded_message: &PublicKey,
|
||||
) -> Result<Option<BlindedSignature>, Error> {
|
||||
Ok(self
|
||||
.blinded_signatures
|
||||
.lock()
|
||||
.await
|
||||
.get(&blinded_message.to_string())
|
||||
.cloned())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::collections::HashMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cashu::nuts::nut02::mint::KeySet;
|
||||
use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof, PublicKey};
|
||||
use cashu::nuts::{BlindedSignature, CurrencyUnit, Id, MintInfo, Proof, PublicKey};
|
||||
use cashu::secret::Secret;
|
||||
use cashu::types::{MeltQuote, MintQuote};
|
||||
pub use memory::MemoryLocalStore;
|
||||
@@ -78,4 +78,14 @@ pub trait LocalStore {
|
||||
async fn get_pending_proof_by_secret(&self, secret: &Secret) -> 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>;
|
||||
|
||||
async fn add_blinded_signature(
|
||||
&self,
|
||||
blinded_message: PublicKey,
|
||||
blinded_signature: BlindedSignature,
|
||||
) -> Result<(), Error>;
|
||||
async fn get_blinded_signature(
|
||||
&self,
|
||||
blinded_message: &PublicKey,
|
||||
) -> Result<Option<BlindedSignature>, Error>;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cashu::dhke::hash_to_curve;
|
||||
use cashu::nuts::{CurrencyUnit, Id, MintInfo, MintKeySet as KeySet, Proof, PublicKey};
|
||||
use cashu::nuts::{
|
||||
BlindedSignature, CurrencyUnit, Id, MintInfo, MintKeySet as KeySet, Proof, PublicKey,
|
||||
};
|
||||
use cashu::secret::Secret;
|
||||
use cashu::types::{MeltQuote, MintQuote};
|
||||
use redb::{Database, ReadableTable, TableDefinition};
|
||||
@@ -20,6 +22,8 @@ const MELT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mel
|
||||
const PENDING_PROOFS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("pending_proofs");
|
||||
const SPENT_PROOFS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("spent_proofs");
|
||||
const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config");
|
||||
// Key is hex blinded_message B_ value is blinded_signature
|
||||
const BLINDED_SIGNATURES: TableDefinition<&str, &str> = TableDefinition::new("blinded_signatures");
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RedbLocalStore {
|
||||
@@ -39,6 +43,7 @@ impl RedbLocalStore {
|
||||
let _ = write_txn.open_table(PENDING_PROOFS_TABLE)?;
|
||||
let _ = write_txn.open_table(SPENT_PROOFS_TABLE)?;
|
||||
let _ = write_txn.open_table(CONFIG_TABLE)?;
|
||||
let _ = write_txn.open_table(BLINDED_SIGNATURES)?;
|
||||
}
|
||||
write_txn.commit()?;
|
||||
|
||||
@@ -391,4 +396,40 @@ impl LocalStore for RedbLocalStore {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_blinded_signature(
|
||||
&self,
|
||||
blinded_message: PublicKey,
|
||||
blinded_signature: BlindedSignature,
|
||||
) -> Result<(), Error> {
|
||||
let db = self.db.lock().await;
|
||||
let write_txn = db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(BLINDED_SIGNATURES)?;
|
||||
table.insert(
|
||||
blinded_message.to_string().as_str(),
|
||||
serde_json::to_string(&blinded_signature)?.as_str(),
|
||||
)?;
|
||||
}
|
||||
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_blinded_signature(
|
||||
&self,
|
||||
blinded_message: &PublicKey,
|
||||
) -> Result<Option<BlindedSignature>, Error> {
|
||||
let db = self.db.lock().await;
|
||||
let read_txn = db.begin_read()?;
|
||||
let table = read_txn.open_table(BLINDED_SIGNATURES)?;
|
||||
|
||||
if let Some(blinded_signature) = table.get(blinded_message.to_string().as_str())? {
|
||||
return Ok(serde_json::from_str(blinded_signature.value())?);
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ pub enum Error {
|
||||
UnknownSecretKind,
|
||||
#[error("Cannot have multiple units")]
|
||||
MultipleUnits,
|
||||
#[error("Blinded Message is already signed")]
|
||||
BlindedMessageAlreadySigned,
|
||||
}
|
||||
|
||||
impl From<Error> for ErrorResponse {
|
||||
@@ -282,6 +284,17 @@ impl Mint {
|
||||
&mut self,
|
||||
mint_request: nut04::MintBolt11Request,
|
||||
) -> Result<nut04::MintBolt11Response, Error> {
|
||||
for blinded_message in &mint_request.outputs {
|
||||
if self
|
||||
.localstore
|
||||
.get_blinded_signature(&blinded_message.b)
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::BlindedMessageAlreadySigned);
|
||||
}
|
||||
}
|
||||
|
||||
let quote = self
|
||||
.localstore
|
||||
.get_mint_quote(&mint_request.quote)
|
||||
@@ -295,7 +308,11 @@ impl Mint {
|
||||
let mut blind_signatures = Vec::with_capacity(mint_request.outputs.len());
|
||||
|
||||
for blinded_message in mint_request.outputs {
|
||||
blind_signatures.push(self.blind_sign(&blinded_message).await?);
|
||||
let blinded_signature = self.blind_sign(&blinded_message).await?;
|
||||
self.localstore
|
||||
.add_blinded_signature(blinded_message.b, blinded_signature.clone())
|
||||
.await?;
|
||||
blind_signatures.push(blinded_signature);
|
||||
}
|
||||
|
||||
Ok(nut04::MintBolt11Response {
|
||||
@@ -349,6 +366,17 @@ impl Mint {
|
||||
&mut self,
|
||||
swap_request: SwapRequest,
|
||||
) -> Result<SwapResponse, Error> {
|
||||
for blinded_message in &swap_request.outputs {
|
||||
if self
|
||||
.localstore
|
||||
.get_blinded_signature(&blinded_message.b)
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::BlindedMessageAlreadySigned);
|
||||
}
|
||||
}
|
||||
|
||||
let proofs_total = swap_request.input_amount();
|
||||
|
||||
let output_total = swap_request.output_amount();
|
||||
@@ -416,9 +444,12 @@ impl Mint {
|
||||
|
||||
let mut promises = Vec::with_capacity(swap_request.outputs.len());
|
||||
|
||||
for output in swap_request.outputs {
|
||||
let promise = self.blind_sign(&output).await?;
|
||||
promises.push(promise);
|
||||
for blinded_message in swap_request.outputs {
|
||||
let blinded_signature = self.blind_sign(&blinded_message).await?;
|
||||
self.localstore
|
||||
.add_blinded_signature(blinded_message.b, blinded_signature.clone())
|
||||
.await?;
|
||||
promises.push(blinded_signature);
|
||||
}
|
||||
|
||||
Ok(SwapResponse::new(promises))
|
||||
@@ -619,6 +650,19 @@ impl Mint {
|
||||
) -> Result<MeltBolt11Response, Error> {
|
||||
self.verify_melt_request(melt_request).await?;
|
||||
|
||||
if let Some(outputs) = &melt_request.outputs {
|
||||
for blinded_message in outputs {
|
||||
if self
|
||||
.localstore
|
||||
.get_blinded_signature(&blinded_message.b)
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::BlindedMessageAlreadySigned);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for input in &melt_request.inputs {
|
||||
self.localstore.add_spent_proof(input.clone()).await?;
|
||||
}
|
||||
@@ -647,8 +691,11 @@ impl Mint {
|
||||
let mut blinded_message = blinded_message;
|
||||
blinded_message.amount = *amount;
|
||||
|
||||
let signature = self.blind_sign(&blinded_message).await?;
|
||||
change_sigs.push(signature)
|
||||
let blinded_signature = self.blind_sign(&blinded_message).await?;
|
||||
self.localstore
|
||||
.add_blinded_signature(blinded_message.b, blinded_signature.clone())
|
||||
.await?;
|
||||
change_sigs.push(blinded_signature)
|
||||
}
|
||||
|
||||
change = Some(change_sigs);
|
||||
|
||||
Reference in New Issue
Block a user