refactor: get spent and pending proofs by list of secrets or ys

This commit is contained in:
thesimplekid
2024-07-16 00:40:28 +01:00
parent 7bdc9280d2
commit ed007c475e
5 changed files with 338 additions and 147 deletions

View File

@@ -518,34 +518,56 @@ impl MintDatabase for MintRedbDatabase {
Ok(()) Ok(())
} }
async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err> { async fn get_spent_proofs_by_ys(
&self,
ys: &[PublicKey],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let db = self.db.lock().await; let db = self.db.lock().await;
let read_txn = db.begin_read().map_err(Error::from)?; let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn let table = read_txn
.open_table(SPENT_PROOFS_TABLE) .open_table(SPENT_PROOFS_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
let mut proofs = Vec::with_capacity(ys.len());
for y in ys {
match table.get(y.to_bytes()).map_err(Error::from)? { match table.get(y.to_bytes()).map_err(Error::from)? {
Some(proof) => Ok(serde_json::from_str(proof.value()).map_err(Error::from)?), Some(proof) => proofs.push(Some(
None => Ok(None), serde_json::from_str(proof.value()).map_err(Error::from)?,
)),
None => proofs.push(None),
} }
} }
async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result<Option<Proof>, Self::Err> { Ok(proofs)
}
async fn get_spent_proofs_by_secrets(
&self,
secrets: &[Secret],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let db = self.db.lock().await; let db = self.db.lock().await;
let read_txn = db.begin_read().map_err(Error::from)?; let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn let table = read_txn
.open_table(SPENT_PROOFS_TABLE) .open_table(SPENT_PROOFS_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
let mut proofs = Vec::with_capacity(secrets.len());
for secret in secrets {
let y: PublicKey = hash_to_curve(&secret.to_bytes())?; let y: PublicKey = hash_to_curve(&secret.to_bytes())?;
match table.get(y.to_bytes()).map_err(Error::from)? { match table.get(y.to_bytes()).map_err(Error::from)? {
Some(proof) => Ok(serde_json::from_str(proof.value()).map_err(Error::from)?), Some(proof) => proofs.push(Some(
None => Ok(None), serde_json::from_str(proof.value()).map_err(Error::from)?,
)),
None => proofs.push(None),
} }
} }
Ok(proofs)
}
async fn add_pending_proofs(&self, proofs: Vec<Proof>) -> Result<(), Self::Err> { async fn add_pending_proofs(&self, proofs: Vec<Proof>) -> Result<(), Self::Err> {
let db = self.db.lock().await; let db = self.db.lock().await;
@@ -569,35 +591,54 @@ impl MintDatabase for MintRedbDatabase {
Ok(()) Ok(())
} }
async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err> { async fn get_pending_proofs_by_ys(
&self,
ys: &[PublicKey],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let db = self.db.lock().await; let db = self.db.lock().await;
let read_txn = db.begin_read().map_err(Error::from)?; let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn let table = read_txn
.open_table(PENDING_PROOFS_TABLE) .open_table(PENDING_PROOFS_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
let mut proofs = Vec::with_capacity(ys.len());
for y in ys {
match table.get(y.to_bytes()).map_err(Error::from)? {
Some(proof) => proofs.push(Some(
serde_json::from_str(proof.value()).map_err(Error::from)?,
)),
None => proofs.push(None),
}
}
Ok(proofs)
}
async fn get_pending_proofs_by_secrets(
&self,
secrets: &[Secret],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let db = self.db.lock().await;
let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn
.open_table(PENDING_PROOFS_TABLE)
.map_err(Error::from)?;
let mut proofs = Vec::with_capacity(secrets.len());
for secret in secrets {
let y: PublicKey = hash_to_curve(&secret.to_bytes())?;
match table.get(y.to_bytes()).map_err(Error::from)? { match table.get(y.to_bytes()).map_err(Error::from)? {
Some(proof) => Ok(serde_json::from_str(proof.value()).map_err(Error::from)?), Some(proof) => proofs.push(Some(
None => Ok(None), serde_json::from_str(proof.value()).map_err(Error::from)?,
)),
None => proofs.push(None),
} }
} }
async fn get_pending_proof_by_secret( Ok(proofs)
&self,
secret: &Secret,
) -> Result<Option<Proof>, Self::Err> {
let db = self.db.lock().await;
let read_txn = db.begin_read().map_err(Error::from)?;
let table = read_txn
.open_table(PENDING_PROOFS_TABLE)
.map_err(Error::from)?;
let secret_hash = hash_to_curve(&secret.to_bytes())?;
match table.get(secret_hash.to_bytes()).map_err(Error::from)? {
Some(proof) => Ok(serde_json::from_str(proof.value()).map_err(Error::from)?),
None => Ok(None),
}
} }
async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> { async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> {

View File

@@ -506,7 +506,15 @@ VALUES (?, ?, ?, ?, ?, ?, ?);
transaction.commit().await.map_err(Error::from)?; transaction.commit().await.map_err(Error::from)?;
Ok(()) Ok(())
} }
async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result<Option<Proof>, Self::Err> { async fn get_spent_proofs_by_secrets(
&self,
secrets: &[Secret],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let mut proofs = Vec::with_capacity(secrets.len());
for secret in secrets {
let rec = sqlx::query( let rec = sqlx::query(
r#" r#"
SELECT * SELECT *
@@ -516,20 +524,31 @@ AND state="SPENT";
"#, "#,
) )
.bind(secret.to_string()) .bind(secret.to_string())
.fetch_one(&self.pool) .fetch_one(&mut transaction)
.await; .await;
let rec = match rec { match rec {
Ok(rec) => rec, Ok(rec) => {
proofs.push(Some(sqlite_row_to_proof(rec)?));
}
Err(err) => match err { Err(err) => match err {
sqlx::Error::RowNotFound => return Ok(None), sqlx::Error::RowNotFound => proofs.push(None),
_ => return Err(Error::SQLX(err).into()), _ => return Err(Error::SQLX(err).into()),
}, },
}; };
Ok(Some(sqlite_row_to_proof(rec)?))
} }
async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err> { transaction.commit().await.map_err(Error::from)?;
Ok(proofs)
}
async fn get_spent_proofs_by_ys(
&self,
ys: &[PublicKey],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let mut proofs = Vec::with_capacity(ys.len());
for y in ys {
let rec = sqlx::query( let rec = sqlx::query(
r#" r#"
SELECT * SELECT *
@@ -539,18 +558,23 @@ AND state="SPENT";
"#, "#,
) )
.bind(y.to_bytes().to_vec()) .bind(y.to_bytes().to_vec())
.fetch_one(&self.pool) .fetch_one(&mut transaction)
.await; .await;
let rec = match rec { match rec {
Ok(rec) => rec, Ok(rec) => {
proofs.push(Some(sqlite_row_to_proof(rec)?));
}
Err(err) => match err { Err(err) => match err {
sqlx::Error::RowNotFound => return Ok(None), sqlx::Error::RowNotFound => proofs.push(None),
_ => return Err(Error::SQLX(err).into()), _ => return Err(Error::SQLX(err).into()),
}, },
}; };
}
Ok(Some(sqlite_row_to_proof(rec)?)) transaction.commit().await.map_err(Error::from)?;
Ok(proofs)
} }
async fn add_pending_proofs(&self, proofs: Proofs) -> Result<(), Self::Err> { async fn add_pending_proofs(&self, proofs: Proofs) -> Result<(), Self::Err> {
@@ -578,10 +602,15 @@ VALUES (?, ?, ?, ?, ?, ?, ?);
Ok(()) Ok(())
} }
async fn get_pending_proof_by_secret( async fn get_pending_proofs_by_secrets(
&self, &self,
secret: &Secret, secrets: &[Secret],
) -> Result<Option<Proof>, Self::Err> { ) -> Result<Vec<Option<Proof>>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let mut proofs = Vec::with_capacity(secrets.len());
for secret in secrets {
let rec = sqlx::query( let rec = sqlx::query(
r#" r#"
SELECT * SELECT *
@@ -591,20 +620,30 @@ AND state="PENDING";
"#, "#,
) )
.bind(secret.to_string()) .bind(secret.to_string())
.fetch_one(&self.pool) .fetch_one(&mut transaction)
.await; .await;
match rec {
let rec = match rec { Ok(rec) => {
Ok(rec) => rec, proofs.push(Some(sqlite_row_to_proof(rec)?));
}
Err(err) => match err { Err(err) => match err {
sqlx::Error::RowNotFound => return Ok(None), sqlx::Error::RowNotFound => proofs.push(None),
_ => return Err(Error::SQLX(err).into()), _ => return Err(Error::SQLX(err).into()),
}, },
}; };
Ok(Some(sqlite_row_to_proof(rec)?))
} }
async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err> { transaction.commit().await.map_err(Error::from)?;
Ok(proofs)
}
async fn get_pending_proofs_by_ys(
&self,
ys: &[PublicKey],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let mut proofs = Vec::with_capacity(ys.len());
for y in ys {
let rec = sqlx::query( let rec = sqlx::query(
r#" r#"
SELECT * SELECT *
@@ -614,17 +653,21 @@ AND state="PENDING";
"#, "#,
) )
.bind(y.to_bytes().to_vec()) .bind(y.to_bytes().to_vec())
.fetch_one(&self.pool) .fetch_one(&mut transaction)
.await; .await;
let rec = match rec { match rec {
Ok(rec) => rec, Ok(rec) => {
proofs.push(Some(sqlite_row_to_proof(rec)?));
}
Err(err) => match err { Err(err) => match err {
sqlx::Error::RowNotFound => return Ok(None), sqlx::Error::RowNotFound => proofs.push(None),
_ => return Err(Error::SQLX(err).into()), _ => return Err(Error::SQLX(err).into()),
}, },
}; };
Ok(Some(sqlite_row_to_proof(rec)?)) }
Ok(proofs)
} }
async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> { async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;

View File

@@ -223,17 +223,40 @@ impl MintDatabase for MintMemoryDatabase {
Ok(()) Ok(())
} }
async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result<Option<Proof>, Self::Err> { async fn get_spent_proofs_by_secrets(
Ok(self &self,
.spent_proofs secrets: &[Secret],
.read() ) -> Result<Vec<Option<Proof>>, Self::Err> {
.await let spent_proofs = self.spent_proofs.read().await;
.get(&hash_to_curve(&secret.to_bytes())?.to_bytes())
.cloned()) let mut proofs = Vec::with_capacity(secrets.len());
for secret in secrets {
let y = hash_to_curve(&secret.to_bytes())?;
let proof = spent_proofs.get(&y.to_bytes()).cloned();
proofs.push(proof);
} }
async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err> { Ok(proofs)
Ok(self.spent_proofs.read().await.get(&y.to_bytes()).cloned()) }
async fn get_spent_proofs_by_ys(
&self,
ys: &[PublicKey],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let spent_proofs = self.spent_proofs.read().await;
let mut proofs = Vec::with_capacity(ys.len());
for y in ys {
let proof = spent_proofs.get(&y.to_bytes()).cloned();
proofs.push(proof);
}
Ok(proofs)
} }
async fn add_pending_proofs(&self, pending_proofs: Proofs) -> Result<(), Self::Err> { async fn add_pending_proofs(&self, pending_proofs: Proofs) -> Result<(), Self::Err> {
@@ -245,21 +268,40 @@ impl MintDatabase for MintMemoryDatabase {
Ok(()) Ok(())
} }
async fn get_pending_proof_by_secret( async fn get_pending_proofs_by_secrets(
&self, &self,
secret: &Secret, secrets: &[Secret],
) -> Result<Option<Proof>, Self::Err> { ) -> Result<Vec<Option<Proof>>, Self::Err> {
let secret_point = hash_to_curve(&secret.to_bytes())?; let spent_proofs = self.pending_proofs.read().await;
Ok(self
.pending_proofs let mut proofs = Vec::with_capacity(secrets.len());
.read()
.await for secret in secrets {
.get(&secret_point.to_bytes()) let y = hash_to_curve(&secret.to_bytes())?;
.cloned())
let proof = spent_proofs.get(&y.to_bytes()).cloned();
proofs.push(proof);
} }
async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err> { Ok(proofs)
Ok(self.pending_proofs.read().await.get(&y.to_bytes()).cloned()) }
async fn get_pending_proofs_by_ys(
&self,
ys: &[PublicKey],
) -> Result<Vec<Option<Proof>>, Self::Err> {
let spent_proofs = self.pending_proofs.read().await;
let mut proofs = Vec::with_capacity(ys.len());
for y in ys {
let proof = spent_proofs.get(&y.to_bytes()).cloned();
proofs.push(proof);
}
Ok(proofs)
} }
async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> { async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> {

View File

@@ -219,20 +219,29 @@ pub trait MintDatabase {
/// Add spent [`Proofs`] /// Add spent [`Proofs`]
async fn add_spent_proofs(&self, proof: Proofs) -> Result<(), Self::Err>; async fn add_spent_proofs(&self, proof: Proofs) -> Result<(), Self::Err>;
/// Get spent [`Proof`] by secret /// Get spent [`Proofs`] by secrets
async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result<Option<Proof>, Self::Err>; async fn get_spent_proofs_by_secrets(
/// Get spent [`Proof`] by y &self,
async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err>; secret: &[Secret],
) -> Result<Vec<Option<Proof>>, Self::Err>;
/// Get spent [`Proofs`] by ys
async fn get_spent_proofs_by_ys(
&self,
y: &[PublicKey],
) -> Result<Vec<Option<Proof>>, Self::Err>;
/// Add pending [`Proofs`] /// Add pending [`Proofs`]
async fn add_pending_proofs(&self, proof: Proofs) -> Result<(), Self::Err>; async fn add_pending_proofs(&self, proof: Proofs) -> Result<(), Self::Err>;
/// Get pending [`Proof`] by secret /// Get pending [`Proofs`] by secrets
async fn get_pending_proof_by_secret( async fn get_pending_proofs_by_secrets(
&self, &self,
secret: &Secret, secrets: &[Secret],
) -> Result<Option<Proof>, Self::Err>; ) -> Result<Vec<Option<Proof>>, Self::Err>;
/// Get pending [`Proof`] by y /// Get pending [`Proofs`] by ys
async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Self::Err>; async fn get_pending_proofs_by_ys(
&self,
ys: &[PublicKey],
) -> Result<Vec<Option<Proof>>, Self::Err>;
/// Remove pending [`Proofs`] /// Remove pending [`Proofs`]
async fn remove_pending_proofs(&self, secret: Vec<&Secret>) -> Result<(), Self::Err>; async fn remove_pending_proofs(&self, secret: Vec<&Secret>) -> Result<(), Self::Err>;

View File

@@ -606,15 +606,43 @@ impl Mint {
let proof_count = swap_request.inputs.len(); let proof_count = swap_request.inputs.len();
let secrets: HashSet<[u8; 33]> = swap_request let secrets: Vec<PublicKey> = swap_request
.inputs .inputs
.iter() .iter()
.flat_map(|p| hash_to_curve(&p.secret.to_bytes())) .flat_map(|p| hash_to_curve(&p.secret.to_bytes()))
.map(|p| p.to_bytes())
.collect(); .collect();
let pending_proofs: Proofs = self
.localstore
.get_pending_proofs_by_ys(&secrets)
.await?
.into_iter()
.flatten()
.collect();
if !pending_proofs.is_empty() {
return Err(Error::TokenPending);
}
let spent_proofs: Proofs = self
.localstore
.get_spent_proofs_by_ys(&secrets)
.await?
.into_iter()
.flatten()
.collect();
if !spent_proofs.is_empty() {
return Err(Error::TokenAlreadySpent);
}
// Check that there are no duplicate proofs in request // Check that there are no duplicate proofs in request
if secrets.len().ne(&proof_count) { if secrets
.iter()
.collect::<HashSet<&PublicKey>>()
.len()
.ne(&proof_count)
{
return Err(Error::DuplicateProofs); return Err(Error::DuplicateProofs);
} }
@@ -709,16 +737,6 @@ impl Mint {
} }
} }
let y: PublicKey = hash_to_curve(&proof.secret.to_bytes())?;
if self.localstore.get_spent_proof_by_y(&y).await?.is_some() {
return Err(Error::TokenAlreadySpent);
}
if self.localstore.get_pending_proof_by_y(&y).await?.is_some() {
return Err(Error::TokenPending);
}
self.ensure_keyset_loaded(&proof.keyset_id).await?; self.ensure_keyset_loaded(&proof.keyset_id).await?;
let keysets = self.keysets.read().await; let keysets = self.keysets.read().await;
let keyset = keysets.get(&proof.keyset_id).ok_or(Error::UnknownKeySet)?; let keyset = keysets.get(&proof.keyset_id).ok_or(Error::UnknownKeySet)?;
@@ -739,13 +757,28 @@ impl Mint {
) -> Result<CheckStateResponse, Error> { ) -> Result<CheckStateResponse, Error> {
let mut states = Vec::with_capacity(check_state.ys.len()); let mut states = Vec::with_capacity(check_state.ys.len());
for y in &check_state.ys { let spent_proofs = self
let state = if self.localstore.get_spent_proof_by_y(y).await?.is_some() { .localstore
.get_spent_proofs_by_ys(&check_state.ys)
.await?;
let pending_proofs = self
.localstore
.get_pending_proofs_by_ys(&check_state.ys)
.await?;
for ((spent, pending), y) in spent_proofs
.iter()
.zip(&pending_proofs)
.zip(&check_state.ys)
{
let state = match (spent, pending) {
(None, None) => State::Unspent,
(Some(_), None) => State::Spent,
(None, Some(_)) => State::Pending,
(Some(_), Some(_)) => {
tracing::error!("Proof should not be both pending and spent. Assuming Spent");
State::Spent State::Spent
} else if self.localstore.get_pending_proof_by_y(y).await?.is_some() { }
State::Pending
} else {
State::Unspent
}; };
states.push(ProofState { states.push(ProofState {
@@ -763,6 +796,41 @@ impl Mint {
&self, &self,
melt_request: &MeltBolt11Request, melt_request: &MeltBolt11Request,
) -> Result<MeltQuote, Error> { ) -> Result<MeltQuote, Error> {
let secrets: Vec<PublicKey> = melt_request
.inputs
.iter()
.flat_map(|p| hash_to_curve(&p.secret.to_bytes()))
.collect();
// Ensure proofs are unique and not being double spent
if melt_request.inputs.len() != secrets.iter().collect::<HashSet<_>>().len() {
return Err(Error::DuplicateProofs);
}
let pending_proofs: Proofs = self
.localstore
.get_pending_proofs_by_ys(&secrets)
.await?
.into_iter()
.flatten()
.collect();
if !pending_proofs.is_empty() {
return Err(Error::TokenPending);
}
let spent_proofs: Proofs = self
.localstore
.get_spent_proofs_by_ys(&secrets)
.await?
.into_iter()
.flatten()
.collect();
if !spent_proofs.is_empty() {
return Err(Error::TokenAlreadySpent);
}
for proof in &melt_request.inputs { for proof in &melt_request.inputs {
self.verify_proof(proof).await?; self.verify_proof(proof).await?;
} }
@@ -858,18 +926,6 @@ impl Mint {
return Err(Error::MultipleUnits); return Err(Error::MultipleUnits);
} }
let secrets: HashSet<[u8; 33]> = melt_request
.inputs
.iter()
.flat_map(|p| hash_to_curve(&p.secret.to_bytes()))
.map(|p| p.to_bytes())
.collect();
// Ensure proofs are unique and not being double spent
if melt_request.inputs.len().ne(&secrets.len()) {
return Err(Error::DuplicateProofs);
}
// Add proofs to pending // Add proofs to pending
self.localstore self.localstore
.add_pending_proofs(melt_request.inputs.clone()) .add_pending_proofs(melt_request.inputs.clone())