|
@@ -1,52 +1,57 @@
|
|
//! SQLite Mint Auth
|
|
//! SQLite Mint Auth
|
|
|
|
|
|
use std::collections::HashMap;
|
|
use std::collections::HashMap;
|
|
|
|
+use std::ops::DerefMut;
|
|
use std::path::Path;
|
|
use std::path::Path;
|
|
use std::str::FromStr;
|
|
use std::str::FromStr;
|
|
-use std::time::Duration;
|
|
|
|
|
|
|
|
use async_trait::async_trait;
|
|
use async_trait::async_trait;
|
|
use cdk_common::database::{self, MintAuthDatabase};
|
|
use cdk_common::database::{self, MintAuthDatabase};
|
|
use cdk_common::mint::MintKeySetInfo;
|
|
use cdk_common::mint::MintKeySetInfo;
|
|
use cdk_common::nuts::{AuthProof, BlindSignature, Id, PublicKey, State};
|
|
use cdk_common::nuts::{AuthProof, BlindSignature, Id, PublicKey, State};
|
|
use cdk_common::{AuthRequired, ProtectedEndpoint};
|
|
use cdk_common::{AuthRequired, ProtectedEndpoint};
|
|
-use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions};
|
|
|
|
-use sqlx::Row;
|
|
|
|
use tracing::instrument;
|
|
use tracing::instrument;
|
|
|
|
|
|
|
|
+use super::async_rusqlite::AsyncRusqlite;
|
|
use super::{sqlite_row_to_blind_signature, sqlite_row_to_keyset_info};
|
|
use super::{sqlite_row_to_blind_signature, sqlite_row_to_keyset_info};
|
|
|
|
+use crate::column_as_string;
|
|
|
|
+use crate::common::{create_sqlite_pool, migrate};
|
|
|
|
+use crate::mint::async_rusqlite::query;
|
|
use crate::mint::Error;
|
|
use crate::mint::Error;
|
|
|
|
|
|
/// Mint SQLite Database
|
|
/// Mint SQLite Database
|
|
#[derive(Debug, Clone)]
|
|
#[derive(Debug, Clone)]
|
|
pub struct MintSqliteAuthDatabase {
|
|
pub struct MintSqliteAuthDatabase {
|
|
- pool: SqlitePool,
|
|
|
|
|
|
+ pool: AsyncRusqlite,
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#[rustfmt::skip]
|
|
|
|
+mod migrations;
|
|
|
|
+
|
|
impl MintSqliteAuthDatabase {
|
|
impl MintSqliteAuthDatabase {
|
|
/// Create new [`MintSqliteAuthDatabase`]
|
|
/// Create new [`MintSqliteAuthDatabase`]
|
|
- pub async fn new(path: &Path) -> Result<Self, Error> {
|
|
|
|
- let path = path.to_str().ok_or(Error::InvalidDbPath)?;
|
|
|
|
- let db_options = SqliteConnectOptions::from_str(path)?
|
|
|
|
- .busy_timeout(Duration::from_secs(5))
|
|
|
|
- .read_only(false)
|
|
|
|
- .create_if_missing(true)
|
|
|
|
- .auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Full);
|
|
|
|
-
|
|
|
|
- let pool = SqlitePoolOptions::new()
|
|
|
|
- .max_connections(1)
|
|
|
|
- .connect_with(db_options)
|
|
|
|
- .await?;
|
|
|
|
-
|
|
|
|
- Ok(Self { pool })
|
|
|
|
|
|
+ #[cfg(not(feature = "sqlcipher"))]
|
|
|
|
+ pub async fn new<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
|
|
|
|
+ let pool = create_sqlite_pool(path.as_ref().to_str().ok_or(Error::InvalidDbPath)?);
|
|
|
|
+ migrate(pool.get()?.deref_mut(), migrations::MIGRATIONS)?;
|
|
|
|
+
|
|
|
|
+ Ok(Self {
|
|
|
|
+ pool: AsyncRusqlite::new(pool),
|
|
|
|
+ })
|
|
}
|
|
}
|
|
|
|
|
|
- /// Migrate [`MintSqliteAuthDatabase`]
|
|
|
|
- pub async fn migrate(&self) {
|
|
|
|
- sqlx::migrate!("./src/mint/auth/migrations")
|
|
|
|
- .run(&self.pool)
|
|
|
|
- .await
|
|
|
|
- .expect("Could not run migrations");
|
|
|
|
|
|
+ /// Create new [`MintSqliteAuthDatabase`]
|
|
|
|
+ #[cfg(feature = "sqlcipher")]
|
|
|
|
+ pub async fn new<P: AsRef<Path>>(path: P, password: String) -> Result<Self, Error> {
|
|
|
|
+ let pool = create_sqlite_pool(
|
|
|
|
+ path.as_ref().to_str().ok_or(Error::InvalidDbPath)?,
|
|
|
|
+ password,
|
|
|
|
+ );
|
|
|
|
+ migrate(pool.get()?.deref_mut(), migrations::MIGRATIONS)?;
|
|
|
|
+
|
|
|
|
+ Ok(Self {
|
|
|
|
+ pool: AsyncRusqlite::new(pool),
|
|
|
|
+ })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -57,230 +62,156 @@ impl MintAuthDatabase for MintSqliteAuthDatabase {
|
|
#[instrument(skip(self))]
|
|
#[instrument(skip(self))]
|
|
async fn set_active_keyset(&self, id: Id) -> Result<(), Self::Err> {
|
|
async fn set_active_keyset(&self, id: Id) -> Result<(), Self::Err> {
|
|
tracing::info!("Setting auth keyset {id} active");
|
|
tracing::info!("Setting auth keyset {id} active");
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
- let update_res = sqlx::query(
|
|
|
|
|
|
+ query(
|
|
r#"
|
|
r#"
|
|
- UPDATE keyset
|
|
|
|
- SET active = CASE
|
|
|
|
- WHEN id = ? THEN TRUE
|
|
|
|
- ELSE FALSE
|
|
|
|
- END;
|
|
|
|
- "#,
|
|
|
|
|
|
+ UPDATE keyset
|
|
|
|
+ SET active = CASE
|
|
|
|
+ WHEN id = :id THEN TRUE
|
|
|
|
+ ELSE FALSE
|
|
|
|
+ END;
|
|
|
|
+ "#,
|
|
)
|
|
)
|
|
- .bind(id.to_string())
|
|
|
|
- .execute(&mut *transaction)
|
|
|
|
- .await;
|
|
|
|
-
|
|
|
|
- match update_res {
|
|
|
|
- Ok(_) => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
- Ok(())
|
|
|
|
- }
|
|
|
|
- Err(err) => {
|
|
|
|
- tracing::error!("SQLite Could not update keyset");
|
|
|
|
- if let Err(err) = transaction.rollback().await {
|
|
|
|
- tracing::error!("Could not rollback sql transaction: {}", err);
|
|
|
|
- }
|
|
|
|
- Err(Error::from(err).into())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ .bind(":id", id.to_string())
|
|
|
|
+ .execute(&self.pool)
|
|
|
|
+ .await?;
|
|
|
|
+
|
|
|
|
+ Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
async fn get_active_keyset_id(&self) -> Result<Option<Id>, Self::Err> {
|
|
async fn get_active_keyset_id(&self) -> Result<Option<Id>, Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let rec = sqlx::query(
|
|
|
|
|
|
+ Ok(query(
|
|
r#"
|
|
r#"
|
|
-SELECT id
|
|
|
|
-FROM keyset
|
|
|
|
-WHERE active = 1;
|
|
|
|
- "#,
|
|
|
|
|
|
+ SELECT
|
|
|
|
+ id
|
|
|
|
+ FROM
|
|
|
|
+ keyset
|
|
|
|
+ WHERE
|
|
|
|
+ active = 1;
|
|
|
|
+ "#,
|
|
)
|
|
)
|
|
- .fetch_one(&mut *transaction)
|
|
|
|
- .await;
|
|
|
|
-
|
|
|
|
- let rec = match rec {
|
|
|
|
- Ok(rec) => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
- rec
|
|
|
|
- }
|
|
|
|
- Err(err) => match err {
|
|
|
|
- sqlx::Error::RowNotFound => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
- return Ok(None);
|
|
|
|
- }
|
|
|
|
- _ => {
|
|
|
|
- return {
|
|
|
|
- if let Err(err) = transaction.rollback().await {
|
|
|
|
- tracing::error!("Could not rollback sql transaction: {}", err);
|
|
|
|
- }
|
|
|
|
- Err(Error::SQLX(err).into())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- Ok(Some(
|
|
|
|
- Id::from_str(rec.try_get("id").map_err(Error::from)?).map_err(Error::from)?,
|
|
|
|
- ))
|
|
|
|
|
|
+ .pluck(&self.pool)
|
|
|
|
+ .await?
|
|
|
|
+ .map(|id| Ok::<_, Error>(column_as_string!(id, Id::from_str, Id::from_bytes)))
|
|
|
|
+ .transpose()?)
|
|
}
|
|
}
|
|
|
|
|
|
async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err> {
|
|
async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
- let res = sqlx::query(
|
|
|
|
|
|
+ query(
|
|
r#"
|
|
r#"
|
|
-INSERT OR REPLACE INTO keyset
|
|
|
|
-(id, unit, active, valid_from, valid_to, derivation_path, max_order, derivation_path_index)
|
|
|
|
-VALUES (?, ?, ?, ?, ?, ?, ?, ?);
|
|
|
|
|
|
+ INSERT INTO
|
|
|
|
+ keyset (
|
|
|
|
+ id, unit, active, valid_from, valid_to, derivation_path,
|
|
|
|
+ max_order, derivation_path_index
|
|
|
|
+ )
|
|
|
|
+ VALUES (
|
|
|
|
+ :id, :unit, :active, :valid_from, :valid_to, :derivation_path,
|
|
|
|
+ :max_order, :derivation_path_index
|
|
|
|
+ )
|
|
|
|
+ ON CONFLICT(id) DO UPDATE SET
|
|
|
|
+ unit = excluded.unit,
|
|
|
|
+ active = excluded.active,
|
|
|
|
+ valid_from = excluded.valid_from,
|
|
|
|
+ valid_to = excluded.valid_to,
|
|
|
|
+ derivation_path = excluded.derivation_path,
|
|
|
|
+ max_order = excluded.max_order,
|
|
|
|
+ derivation_path_index = excluded.derivation_path_index
|
|
"#,
|
|
"#,
|
|
)
|
|
)
|
|
- .bind(keyset.id.to_string())
|
|
|
|
- .bind(keyset.unit.to_string())
|
|
|
|
- .bind(keyset.active)
|
|
|
|
- .bind(keyset.valid_from as i64)
|
|
|
|
- .bind(keyset.valid_to.map(|v| v as i64))
|
|
|
|
- .bind(keyset.derivation_path.to_string())
|
|
|
|
- .bind(keyset.max_order)
|
|
|
|
- .bind(keyset.derivation_path_index)
|
|
|
|
- .execute(&mut *transaction)
|
|
|
|
- .await;
|
|
|
|
-
|
|
|
|
- match res {
|
|
|
|
- Ok(_) => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
- Ok(())
|
|
|
|
- }
|
|
|
|
- Err(err) => {
|
|
|
|
- tracing::error!("SQLite could not add keyset info");
|
|
|
|
- if let Err(err) = transaction.rollback().await {
|
|
|
|
- tracing::error!("Could not rollback sql transaction: {}", err);
|
|
|
|
- }
|
|
|
|
|
|
+ .bind(":id", keyset.id.to_string())
|
|
|
|
+ .bind(":unit", keyset.unit.to_string())
|
|
|
|
+ .bind(":active", keyset.active)
|
|
|
|
+ .bind(":valid_from", keyset.valid_from as i64)
|
|
|
|
+ .bind(":valid_to", keyset.valid_to.map(|v| v as i64))
|
|
|
|
+ .bind(":derivation_path", keyset.derivation_path.to_string())
|
|
|
|
+ .bind(":max_order", keyset.max_order)
|
|
|
|
+ .bind(":derivation_path_index", keyset.derivation_path_index)
|
|
|
|
+ .execute(&self.pool)
|
|
|
|
+ .await?;
|
|
|
|
|
|
- Err(Error::from(err).into())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err> {
|
|
async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
- let rec = sqlx::query(
|
|
|
|
- r#"
|
|
|
|
-SELECT *
|
|
|
|
-FROM keyset
|
|
|
|
-WHERE id=?;
|
|
|
|
- "#,
|
|
|
|
|
|
+ Ok(query(
|
|
|
|
+ r#"SELECT
|
|
|
|
+ id,
|
|
|
|
+ unit,
|
|
|
|
+ active,
|
|
|
|
+ valid_from,
|
|
|
|
+ valid_to,
|
|
|
|
+ derivation_path,
|
|
|
|
+ derivation_path_index,
|
|
|
|
+ max_order,
|
|
|
|
+ input_fee_ppk
|
|
|
|
+ FROM
|
|
|
|
+ keyset
|
|
|
|
+ WHERE id=:id"#,
|
|
)
|
|
)
|
|
- .bind(id.to_string())
|
|
|
|
- .fetch_one(&mut *transaction)
|
|
|
|
- .await;
|
|
|
|
-
|
|
|
|
- match rec {
|
|
|
|
- Ok(rec) => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
- Ok(Some(sqlite_row_to_keyset_info(rec)?))
|
|
|
|
- }
|
|
|
|
- Err(err) => match err {
|
|
|
|
- sqlx::Error::RowNotFound => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
- return Ok(None);
|
|
|
|
- }
|
|
|
|
- _ => {
|
|
|
|
- tracing::error!("SQLite could not get keyset info");
|
|
|
|
- if let Err(err) = transaction.rollback().await {
|
|
|
|
- tracing::error!("Could not rollback sql transaction: {}", err);
|
|
|
|
- }
|
|
|
|
- return Err(Error::SQLX(err).into());
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- }
|
|
|
|
|
|
+ .bind(":id", id.to_string())
|
|
|
|
+ .fetch_one(&self.pool)
|
|
|
|
+ .await?
|
|
|
|
+ .map(sqlite_row_to_keyset_info)
|
|
|
|
+ .transpose()?)
|
|
}
|
|
}
|
|
|
|
|
|
async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err> {
|
|
async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
- let recs = sqlx::query(
|
|
|
|
- r#"
|
|
|
|
-SELECT *
|
|
|
|
-FROM keyset;
|
|
|
|
- "#,
|
|
|
|
|
|
+ Ok(query(
|
|
|
|
+ r#"SELECT
|
|
|
|
+ id,
|
|
|
|
+ unit,
|
|
|
|
+ active,
|
|
|
|
+ valid_from,
|
|
|
|
+ valid_to,
|
|
|
|
+ derivation_path,
|
|
|
|
+ derivation_path_index,
|
|
|
|
+ max_order,
|
|
|
|
+ input_fee_ppk
|
|
|
|
+ FROM
|
|
|
|
+ keyset
|
|
|
|
+ WHERE id=:id"#,
|
|
)
|
|
)
|
|
- .fetch_all(&mut *transaction)
|
|
|
|
- .await
|
|
|
|
- .map_err(Error::from);
|
|
|
|
-
|
|
|
|
- match recs {
|
|
|
|
- Ok(recs) => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
- Ok(recs
|
|
|
|
- .into_iter()
|
|
|
|
- .map(sqlite_row_to_keyset_info)
|
|
|
|
- .collect::<Result<_, _>>()?)
|
|
|
|
- }
|
|
|
|
- Err(err) => {
|
|
|
|
- tracing::error!("SQLite could not get keyset info");
|
|
|
|
- if let Err(err) = transaction.rollback().await {
|
|
|
|
- tracing::error!("Could not rollback sql transaction: {}", err);
|
|
|
|
- }
|
|
|
|
- Err(err.into())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ .fetch_all(&self.pool)
|
|
|
|
+ .await?
|
|
|
|
+ .into_iter()
|
|
|
|
+ .map(sqlite_row_to_keyset_info)
|
|
|
|
+ .collect::<Result<Vec<_>, _>>()?)
|
|
}
|
|
}
|
|
|
|
|
|
async fn add_proof(&self, proof: AuthProof) -> Result<(), Self::Err> {
|
|
async fn add_proof(&self, proof: AuthProof) -> Result<(), Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
- if let Err(err) = sqlx::query(
|
|
|
|
|
|
+ if let Err(err) = query(
|
|
r#"
|
|
r#"
|
|
-INSERT INTO proof
|
|
|
|
-(y, keyset_id, secret, c, state)
|
|
|
|
-VALUES (?, ?, ?, ?, ?);
|
|
|
|
- "#,
|
|
|
|
|
|
+ INSERT INTO proof
|
|
|
|
+ (y, keyset_id, secret, c, state)
|
|
|
|
+ VALUES
|
|
|
|
+ (:y, :keyset_id, :secret, :c, :state)
|
|
|
|
+ "#,
|
|
)
|
|
)
|
|
- .bind(proof.y()?.to_bytes().to_vec())
|
|
|
|
- .bind(proof.keyset_id.to_string())
|
|
|
|
- .bind(proof.secret.to_string())
|
|
|
|
- .bind(proof.c.to_bytes().to_vec())
|
|
|
|
- .bind("UNSPENT")
|
|
|
|
- .execute(&mut *transaction)
|
|
|
|
|
|
+ .bind(":y", proof.y()?.to_bytes().to_vec())
|
|
|
|
+ .bind(":keyset_id", proof.keyset_id.to_string())
|
|
|
|
+ .bind(":secret", proof.secret.to_string())
|
|
|
|
+ .bind(":c", proof.c.to_bytes().to_vec())
|
|
|
|
+ .bind(":state", "UNSPENT".to_string())
|
|
|
|
+ .execute(&self.pool)
|
|
.await
|
|
.await
|
|
- .map_err(Error::from)
|
|
|
|
{
|
|
{
|
|
tracing::debug!("Attempting to add known proof. Skipping.... {:?}", err);
|
|
tracing::debug!("Attempting to add known proof. Skipping.... {:?}", err);
|
|
}
|
|
}
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err> {
|
|
async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let sql = format!(
|
|
|
|
- "SELECT y, state FROM proof WHERE y IN ({})",
|
|
|
|
- "?,".repeat(ys.len()).trim_end_matches(',')
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- let mut current_states = ys
|
|
|
|
- .iter()
|
|
|
|
- .fold(sqlx::query(&sql), |query, y| {
|
|
|
|
- query.bind(y.to_bytes().to_vec())
|
|
|
|
- })
|
|
|
|
- .fetch_all(&mut *transaction)
|
|
|
|
- .await
|
|
|
|
- .map_err(|err| {
|
|
|
|
- tracing::error!("SQLite could not get state of proof: {err:?}");
|
|
|
|
- Error::SQLX(err)
|
|
|
|
- })?
|
|
|
|
|
|
+ let mut current_states = query(r#"SELECT y, state FROM proof WHERE y IN (:ys)"#)
|
|
|
|
+ .bind_vec(":ys", ys.iter().map(|y| y.to_bytes().to_vec()).collect())
|
|
|
|
+ .fetch_all(&self.pool)
|
|
|
|
+ .await?
|
|
.into_iter()
|
|
.into_iter()
|
|
.map(|row| {
|
|
.map(|row| {
|
|
- PublicKey::from_slice(row.get("y"))
|
|
|
|
- .map_err(Error::from)
|
|
|
|
- .and_then(|y| {
|
|
|
|
- let state: String = row.get("state");
|
|
|
|
- State::from_str(&state)
|
|
|
|
- .map_err(Error::from)
|
|
|
|
- .map(|state| (y, state))
|
|
|
|
- })
|
|
|
|
|
|
+ Ok((
|
|
|
|
+ column_as_string!(&row[0], PublicKey::from_hex, PublicKey::from_slice),
|
|
|
|
+ column_as_string!(&row[1], State::from_str),
|
|
|
|
+ ))
|
|
})
|
|
})
|
|
- .collect::<Result<HashMap<_, _>, _>>()?;
|
|
|
|
|
|
+ .collect::<Result<HashMap<_, _>, Error>>()?;
|
|
|
|
|
|
Ok(ys.iter().map(|y| current_states.remove(y)).collect())
|
|
Ok(ys.iter().map(|y| current_states.remove(y)).collect())
|
|
}
|
|
}
|
|
@@ -290,36 +221,27 @@ VALUES (?, ?, ?, ?, ?);
|
|
y: &PublicKey,
|
|
y: &PublicKey,
|
|
proofs_state: State,
|
|
proofs_state: State,
|
|
) -> Result<Option<State>, Self::Err> {
|
|
) -> Result<Option<State>, Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
|
|
+ let transaction = self.pool.begin().await?;
|
|
|
|
|
|
- // Get current state for single y
|
|
|
|
- let current_state = sqlx::query("SELECT state FROM proof WHERE y = ?")
|
|
|
|
- .bind(y.to_bytes().to_vec())
|
|
|
|
- .fetch_optional(&mut *transaction)
|
|
|
|
- .await
|
|
|
|
- .map_err(|err| {
|
|
|
|
- tracing::error!("SQLite could not get state of proof: {err:?}");
|
|
|
|
- Error::SQLX(err)
|
|
|
|
- })?
|
|
|
|
- .map(|row| {
|
|
|
|
- let state: String = row.get("state");
|
|
|
|
- State::from_str(&state).map_err(Error::from)
|
|
|
|
- })
|
|
|
|
|
|
+ let current_state = query(r#"SELECT state FROM proof WHERE y = :y"#)
|
|
|
|
+ .bind(":y", y.to_bytes().to_vec())
|
|
|
|
+ .pluck(&transaction)
|
|
|
|
+ .await?
|
|
|
|
+ .map(|state| Ok::<_, Error>(column_as_string!(state, State::from_str)))
|
|
.transpose()?;
|
|
.transpose()?;
|
|
|
|
|
|
- // Update state for single y
|
|
|
|
- sqlx::query("UPDATE proof SET state = ? WHERE state != ? AND y = ?")
|
|
|
|
- .bind(proofs_state.to_string())
|
|
|
|
- .bind(State::Spent.to_string())
|
|
|
|
- .bind(y.to_bytes().to_vec())
|
|
|
|
- .execute(&mut *transaction)
|
|
|
|
- .await
|
|
|
|
- .map_err(|err| {
|
|
|
|
- tracing::error!("SQLite could not update proof state: {err:?}");
|
|
|
|
- Error::SQLX(err)
|
|
|
|
- })?;
|
|
|
|
|
|
+ query(r#"UPDATE proof SET state = :new_state WHERE state = :state AND y = :y"#)
|
|
|
|
+ .bind(":y", y.to_bytes().to_vec())
|
|
|
|
+ .bind(
|
|
|
|
+ ":state",
|
|
|
|
+ current_state.as_ref().map(|state| state.to_string()),
|
|
|
|
+ )
|
|
|
|
+ .bind(":new_state", proofs_state.to_string())
|
|
|
|
+ .execute(&transaction)
|
|
|
|
+ .await?;
|
|
|
|
+
|
|
|
|
+ transaction.commit().await?;
|
|
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
Ok(current_state)
|
|
Ok(current_state)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -328,32 +250,27 @@ VALUES (?, ?, ?, ?, ?);
|
|
blinded_messages: &[PublicKey],
|
|
blinded_messages: &[PublicKey],
|
|
blind_signatures: &[BlindSignature],
|
|
blind_signatures: &[BlindSignature],
|
|
) -> Result<(), Self::Err> {
|
|
) -> Result<(), Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
|
|
+ let transaction = self.pool.begin().await?;
|
|
|
|
+
|
|
for (message, signature) in blinded_messages.iter().zip(blind_signatures) {
|
|
for (message, signature) in blinded_messages.iter().zip(blind_signatures) {
|
|
- let res = sqlx::query(
|
|
|
|
|
|
+ query(
|
|
r#"
|
|
r#"
|
|
-INSERT INTO blind_signature
|
|
|
|
-(y, amount, keyset_id, c)
|
|
|
|
-VALUES (?, ?, ?, ?);
|
|
|
|
- "#,
|
|
|
|
|
|
+ INSERT
|
|
|
|
+ INTO blind_signature
|
|
|
|
+ (y, amount, keyset_id, c)
|
|
|
|
+ VALUES
|
|
|
|
+ (:y, :amount, :keyset_id, :c)
|
|
|
|
+ "#,
|
|
)
|
|
)
|
|
- .bind(message.to_bytes().to_vec())
|
|
|
|
- .bind(u64::from(signature.amount) as i64)
|
|
|
|
- .bind(signature.keyset_id.to_string())
|
|
|
|
- .bind(signature.c.to_bytes().to_vec())
|
|
|
|
- .execute(&mut *transaction)
|
|
|
|
- .await;
|
|
|
|
-
|
|
|
|
- if let Err(err) = res {
|
|
|
|
- tracing::error!("SQLite could not add blind signature");
|
|
|
|
- if let Err(err) = transaction.rollback().await {
|
|
|
|
- tracing::error!("Could not rollback sql transaction: {}", err);
|
|
|
|
- }
|
|
|
|
- return Err(Error::SQLX(err).into());
|
|
|
|
- }
|
|
|
|
|
|
+ .bind(":y", message.to_bytes().to_vec())
|
|
|
|
+ .bind(":amount", u64::from(signature.amount) as i64)
|
|
|
|
+ .bind(":keyset_id", signature.keyset_id.to_string())
|
|
|
|
+ .bind(":c", signature.c.to_bytes().to_vec())
|
|
|
|
+ .execute(&transaction)
|
|
|
|
+ .await?;
|
|
}
|
|
}
|
|
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
|
|
+ transaction.commit().await?;
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
@@ -362,32 +279,40 @@ VALUES (?, ?, ?, ?);
|
|
&self,
|
|
&self,
|
|
blinded_messages: &[PublicKey],
|
|
blinded_messages: &[PublicKey],
|
|
) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
|
|
) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let sql = format!(
|
|
|
|
- "SELECT * FROM blind_signature WHERE y IN ({})",
|
|
|
|
- "?,".repeat(blinded_messages.len()).trim_end_matches(',')
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- let mut blinded_signatures = blinded_messages
|
|
|
|
- .iter()
|
|
|
|
- .fold(sqlx::query(&sql), |query, y| {
|
|
|
|
- query.bind(y.to_bytes().to_vec())
|
|
|
|
- })
|
|
|
|
- .fetch_all(&mut *transaction)
|
|
|
|
- .await
|
|
|
|
- .map_err(|err| {
|
|
|
|
- tracing::error!("SQLite could not get state of proof: {err:?}");
|
|
|
|
- Error::SQLX(err)
|
|
|
|
- })?
|
|
|
|
- .into_iter()
|
|
|
|
- .map(|row| {
|
|
|
|
- PublicKey::from_slice(row.get("y"))
|
|
|
|
- .map_err(Error::from)
|
|
|
|
- .and_then(|y| sqlite_row_to_blind_signature(row).map(|blinded| (y, blinded)))
|
|
|
|
- })
|
|
|
|
- .collect::<Result<HashMap<_, _>, _>>()?;
|
|
|
|
-
|
|
|
|
|
|
+ let mut blinded_signatures = query(
|
|
|
|
+ r#"SELECT
|
|
|
|
+ keyset_id,
|
|
|
|
+ amount,
|
|
|
|
+ c,
|
|
|
|
+ dleq_e,
|
|
|
|
+ dleq_s,
|
|
|
|
+ y
|
|
|
|
+ FROM
|
|
|
|
+ blind_signature
|
|
|
|
+ WHERE y IN (:y)
|
|
|
|
+ "#,
|
|
|
|
+ )
|
|
|
|
+ .bind_vec(
|
|
|
|
+ ":y",
|
|
|
|
+ blinded_messages
|
|
|
|
+ .iter()
|
|
|
|
+ .map(|y| y.to_bytes().to_vec())
|
|
|
|
+ .collect(),
|
|
|
|
+ )
|
|
|
|
+ .fetch_all(&self.pool)
|
|
|
|
+ .await?
|
|
|
|
+ .into_iter()
|
|
|
|
+ .map(|mut row| {
|
|
|
|
+ Ok((
|
|
|
|
+ column_as_string!(
|
|
|
|
+ &row.pop().ok_or(Error::InvalidDbResponse)?,
|
|
|
|
+ PublicKey::from_hex,
|
|
|
|
+ PublicKey::from_slice
|
|
|
|
+ ),
|
|
|
|
+ sqlite_row_to_blind_signature(row)?,
|
|
|
|
+ ))
|
|
|
|
+ })
|
|
|
|
+ .collect::<Result<HashMap<_, _>, Error>>()?;
|
|
Ok(blinded_messages
|
|
Ok(blinded_messages
|
|
.iter()
|
|
.iter()
|
|
.map(|y| blinded_signatures.remove(y))
|
|
.map(|y| blinded_signatures.remove(y))
|
|
@@ -398,21 +323,20 @@ VALUES (?, ?, ?, ?);
|
|
&self,
|
|
&self,
|
|
protected_endpoints: HashMap<ProtectedEndpoint, AuthRequired>,
|
|
protected_endpoints: HashMap<ProtectedEndpoint, AuthRequired>,
|
|
) -> Result<(), Self::Err> {
|
|
) -> Result<(), Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
|
|
+ let transaction = self.pool.begin().await?;
|
|
|
|
|
|
for (endpoint, auth) in protected_endpoints.iter() {
|
|
for (endpoint, auth) in protected_endpoints.iter() {
|
|
- if let Err(err) = sqlx::query(
|
|
|
|
|
|
+ if let Err(err) = query(
|
|
r#"
|
|
r#"
|
|
-INSERT OR REPLACE INTO protected_endpoints
|
|
|
|
-(endpoint, auth)
|
|
|
|
-VALUES (?, ?);
|
|
|
|
- "#,
|
|
|
|
|
|
+ INSERT OR REPLACE INTO protected_endpoints
|
|
|
|
+ (endpoint, auth)
|
|
|
|
+ VALUES (:endpoint, :auth);
|
|
|
|
+ "#,
|
|
)
|
|
)
|
|
- .bind(serde_json::to_string(endpoint)?)
|
|
|
|
- .bind(serde_json::to_string(auth)?)
|
|
|
|
- .execute(&mut *transaction)
|
|
|
|
|
|
+ .bind(":endpoint", serde_json::to_string(endpoint)?)
|
|
|
|
+ .bind(":auth", serde_json::to_string(auth)?)
|
|
|
|
+ .execute(&transaction)
|
|
.await
|
|
.await
|
|
- .map_err(Error::from)
|
|
|
|
{
|
|
{
|
|
tracing::debug!(
|
|
tracing::debug!(
|
|
"Attempting to add protected endpoint. Skipping.... {:?}",
|
|
"Attempting to add protected endpoint. Skipping.... {:?}",
|
|
@@ -421,7 +345,7 @@ VALUES (?, ?);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
|
|
+ transaction.commit().await?;
|
|
|
|
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
@@ -429,111 +353,52 @@ VALUES (?, ?);
|
|
&self,
|
|
&self,
|
|
protected_endpoints: Vec<ProtectedEndpoint>,
|
|
protected_endpoints: Vec<ProtectedEndpoint>,
|
|
) -> Result<(), Self::Err> {
|
|
) -> Result<(), Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let sql = format!(
|
|
|
|
- "DELETE FROM protected_endpoints WHERE endpoint IN ({})",
|
|
|
|
- std::iter::repeat("?")
|
|
|
|
- .take(protected_endpoints.len())
|
|
|
|
- .collect::<Vec<_>>()
|
|
|
|
- .join(",")
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- let endpoints = protected_endpoints
|
|
|
|
- .iter()
|
|
|
|
- .map(serde_json::to_string)
|
|
|
|
- .collect::<Result<Vec<_>, _>>()?;
|
|
|
|
-
|
|
|
|
- endpoints
|
|
|
|
- .iter()
|
|
|
|
- .fold(sqlx::query(&sql), |query, endpoint| query.bind(endpoint))
|
|
|
|
- .execute(&mut *transaction)
|
|
|
|
- .await
|
|
|
|
- .map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
|
|
+ query(r#"DELETE FROM protected_endpoints WHERE endpoint IN (:endpoints)"#)
|
|
|
|
+ .bind_vec(
|
|
|
|
+ ":endpoints",
|
|
|
|
+ protected_endpoints
|
|
|
|
+ .iter()
|
|
|
|
+ .map(serde_json::to_string)
|
|
|
|
+ .collect::<Result<_, _>>()?,
|
|
|
|
+ )
|
|
|
|
+ .execute(&self.pool)
|
|
|
|
+ .await?;
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
+
|
|
async fn get_auth_for_endpoint(
|
|
async fn get_auth_for_endpoint(
|
|
&self,
|
|
&self,
|
|
protected_endpoint: ProtectedEndpoint,
|
|
protected_endpoint: ProtectedEndpoint,
|
|
) -> Result<Option<AuthRequired>, Self::Err> {
|
|
) -> Result<Option<AuthRequired>, Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let rec = sqlx::query(
|
|
|
|
- r#"
|
|
|
|
-SELECT *
|
|
|
|
-FROM protected_endpoints
|
|
|
|
-WHERE endpoint=?;
|
|
|
|
- "#,
|
|
|
|
|
|
+ Ok(
|
|
|
|
+ query(r#"SELECT auth FROM protected_endpoints WHERE endpoint = :endpoint"#)
|
|
|
|
+ .bind(":endpoint", serde_json::to_string(&protected_endpoint)?)
|
|
|
|
+ .pluck(&self.pool)
|
|
|
|
+ .await?
|
|
|
|
+ .map(|auth| {
|
|
|
|
+ Ok::<_, Error>(column_as_string!(
|
|
|
|
+ auth,
|
|
|
|
+ serde_json::from_str,
|
|
|
|
+ serde_json::from_slice
|
|
|
|
+ ))
|
|
|
|
+ })
|
|
|
|
+ .transpose()?,
|
|
)
|
|
)
|
|
- .bind(serde_json::to_string(&protected_endpoint)?)
|
|
|
|
- .fetch_one(&mut *transaction)
|
|
|
|
- .await;
|
|
|
|
-
|
|
|
|
- match rec {
|
|
|
|
- Ok(rec) => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let auth: String = rec.try_get("auth").map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- Ok(Some(serde_json::from_str(&auth)?))
|
|
|
|
- }
|
|
|
|
- Err(err) => match err {
|
|
|
|
- sqlx::Error::RowNotFound => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
- return Ok(None);
|
|
|
|
- }
|
|
|
|
- _ => {
|
|
|
|
- return {
|
|
|
|
- if let Err(err) = transaction.rollback().await {
|
|
|
|
- tracing::error!("Could not rollback sql transaction: {}", err);
|
|
|
|
- }
|
|
|
|
- Err(Error::SQLX(err).into())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
async fn get_auth_for_endpoints(
|
|
async fn get_auth_for_endpoints(
|
|
&self,
|
|
&self,
|
|
) -> Result<HashMap<ProtectedEndpoint, Option<AuthRequired>>, Self::Err> {
|
|
) -> Result<HashMap<ProtectedEndpoint, Option<AuthRequired>>, Self::Err> {
|
|
- let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let recs = sqlx::query(
|
|
|
|
- r#"
|
|
|
|
-SELECT *
|
|
|
|
-FROM protected_endpoints
|
|
|
|
- "#,
|
|
|
|
- )
|
|
|
|
- .fetch_all(&mut *transaction)
|
|
|
|
- .await;
|
|
|
|
-
|
|
|
|
- match recs {
|
|
|
|
- Ok(recs) => {
|
|
|
|
- transaction.commit().await.map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let mut endpoints = HashMap::new();
|
|
|
|
-
|
|
|
|
- for rec in recs {
|
|
|
|
- let auth: String = rec.try_get("auth").map_err(Error::from)?;
|
|
|
|
- let endpoint: String = rec.try_get("endpoint").map_err(Error::from)?;
|
|
|
|
-
|
|
|
|
- let endpoint: ProtectedEndpoint = serde_json::from_str(&endpoint)?;
|
|
|
|
- let auth: AuthRequired = serde_json::from_str(&auth)?;
|
|
|
|
-
|
|
|
|
- endpoints.insert(endpoint, Some(auth));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Ok(endpoints)
|
|
|
|
- }
|
|
|
|
- Err(err) => {
|
|
|
|
- tracing::error!("SQLite could not get protected endpoints");
|
|
|
|
- if let Err(err) = transaction.rollback().await {
|
|
|
|
- tracing::error!("Could not rollback sql transaction: {}", err);
|
|
|
|
- }
|
|
|
|
- Err(Error::from(err).into())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ Ok(query(r#"SELECT endpoint, auth FROM protected_endpoints"#)
|
|
|
|
+ .fetch_all(&self.pool)
|
|
|
|
+ .await?
|
|
|
|
+ .into_iter()
|
|
|
|
+ .map(|row| {
|
|
|
|
+ let endpoint =
|
|
|
|
+ column_as_string!(&row[0], serde_json::from_str, serde_json::from_slice);
|
|
|
|
+ let auth = column_as_string!(&row[1], serde_json::from_str, serde_json::from_slice);
|
|
|
|
+ Ok((endpoint, Some(auth)))
|
|
|
|
+ })
|
|
|
|
+ .collect::<Result<HashMap<_, _>, Error>>()?)
|
|
}
|
|
}
|
|
}
|
|
}
|