feat(mint): store blinded messages and sigs

This commit is contained in:
thesimplekid
2024-03-08 21:37:09 +00:00
parent 9c0e058541
commit 27677f881a
4 changed files with 134 additions and 9 deletions

View File

@@ -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())
}
}

View File

@@ -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>;
}

View File

@@ -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)
}
}

View File

@@ -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);