credential storage for spending on client
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::CoinIndicesSignature;
|
||||
use crate::models::StoredIssuedCredential;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
@@ -14,7 +15,6 @@ pub struct CoconutCredentialManager {
|
||||
#[derive(Default)]
|
||||
struct CoconutCredentialManagerInner {
|
||||
credentials: Vec<StoredIssuedCredential>,
|
||||
credential_usage: Vec<CredentialUsage>,
|
||||
_next_id: i64,
|
||||
}
|
||||
|
||||
@@ -51,56 +51,33 @@ impl CoconutCredentialManager {
|
||||
credential_type,
|
||||
epoch_id,
|
||||
expired: false,
|
||||
consumed: false,
|
||||
})
|
||||
}
|
||||
|
||||
async fn bandwidth_voucher_spent(&self, id: i64) -> bool {
|
||||
self.inner
|
||||
.read()
|
||||
.await
|
||||
.credential_usage
|
||||
.iter()
|
||||
.any(|c| c.credential_id == id)
|
||||
}
|
||||
|
||||
async fn freepass_spent(&self, id: i64, gateway_id: &str) -> bool {
|
||||
self.inner
|
||||
.read()
|
||||
.await
|
||||
.credential_usage
|
||||
.iter()
|
||||
.any(|c| c.credential_id == id && c.gateway_id_bs58 == gateway_id)
|
||||
}
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
pub async fn get_next_unspect_bandwidth_voucher(&self) -> Option<StoredIssuedCredential> {
|
||||
pub async fn get_next_unspent_ticketbook(&self) -> Option<StoredIssuedCredential> {
|
||||
let guard = self.inner.read().await;
|
||||
for credential in guard
|
||||
.credentials
|
||||
.iter()
|
||||
.filter(|c| c.credential_type == "BandwidthVoucher")
|
||||
.filter(|c| c.credential_type == "TicketBook")
|
||||
{
|
||||
if !self.bandwidth_voucher_spent(credential.id).await {
|
||||
if !credential.consumed && !credential.expired {
|
||||
return Some(credential.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn get_next_unspect_freepass(
|
||||
&self,
|
||||
gateway_id: &str,
|
||||
) -> Option<StoredIssuedCredential> {
|
||||
pub async fn get_next_unspent_freepass(&self) -> Option<StoredIssuedCredential> {
|
||||
let guard = self.inner.read().await;
|
||||
for credential in guard
|
||||
.credentials
|
||||
.iter()
|
||||
.filter(|c| c.credential_type == "FreeBandwidthPass")
|
||||
{
|
||||
if credential.expired {
|
||||
continue;
|
||||
}
|
||||
if !self.freepass_spent(credential.id, gateway_id).await {
|
||||
if !credential.consumed && !credential.expired {
|
||||
return Some(credential.clone());
|
||||
}
|
||||
}
|
||||
@@ -112,8 +89,21 @@ impl CoconutCredentialManager {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id`: Database id.
|
||||
pub async fn consume_coconut_credential(&self, id: i64, gateway_id: &str) {
|
||||
pub async fn consume_coconut_credential(&self, id: i64) {
|
||||
let mut guard = self.inner.write().await;
|
||||
if let Some(cred) = guard.credentials.get_mut(id as usize) {
|
||||
cred.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_issued_credential(&self, credential_data: &[u8], id: i64, consumed: bool) {
|
||||
let mut guard = self.inner.write().await;
|
||||
if let Some(cred) = guard.credentials.get_mut(id as usize) {
|
||||
cred.credential_data = credential_data.to_vec();
|
||||
cred.consumed = consumed;
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts provided coin_indices_signatures into the database.
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@@ -28,50 +28,45 @@ impl CoconutCredentialManager {
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO coconut_credentials(serialization_revision, credential_type, credential_data, epoch_id, expired)
|
||||
VALUES (?, ?, ?, ?, false)
|
||||
INSERT INTO ecash_credentials(serialization_revision, credential_type, credential_data, epoch_id, expired, consumed)
|
||||
VALUES (?, ?, ?, ?, false, false)
|
||||
"#,
|
||||
serialization_revision, credential_type, credential_data, epoch_id
|
||||
).execute(&self.connection_pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_next_unspect_freepass(
|
||||
pub async fn get_next_unspent_freepass(
|
||||
&self,
|
||||
gateway_id: &str,
|
||||
) -> Result<Option<StoredIssuedCredential>, sqlx::Error> {
|
||||
// get a credential of freepass type that doesn't appear in `credential_usage` for the provided gateway_id
|
||||
// get a credential of freepass type
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT *
|
||||
FROM coconut_credentials
|
||||
WHERE coconut_credentials.credential_type == "FreeBandwidthPass" AND coconut_credentials.expired = false
|
||||
AND NOT EXISTS (SELECT 1
|
||||
FROM credential_usage
|
||||
WHERE credential_usage.credential_id = coconut_credentials.id
|
||||
AND credential_usage.gateway_id_bs58 == ?)
|
||||
ORDER BY coconut_credentials.id
|
||||
FROM ecash_credentials
|
||||
WHERE credential_type == "FreeBandwidthPass"
|
||||
AND expired = false
|
||||
AND consumed = false
|
||||
ORDER BY id ASC
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
.bind(gateway_id)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_next_unspect_bandwidth_voucher(
|
||||
pub async fn get_next_unspent_ticketbook(
|
||||
&self,
|
||||
) -> Result<Option<StoredIssuedCredential>, sqlx::Error> {
|
||||
// get a credential of bandwidth voucher type that doesn't appear in `credential_usage` for any gateway_id
|
||||
// get a credential of bandwidth voucher type
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT *
|
||||
FROM coconut_credentials
|
||||
WHERE coconut_credentials.credential_type == "BandwidthVoucher"
|
||||
AND NOT EXISTS (SELECT 1
|
||||
FROM credential_usage
|
||||
WHERE credential_usage.credential_id = coconut_credentials.id)
|
||||
ORDER BY coconut_credentials.id
|
||||
FROM ecash_credentials
|
||||
WHERE credential_type == "TicketBook"
|
||||
AND expired = false
|
||||
AND consumed = false
|
||||
ORDER BY id ASC
|
||||
LIMIT 1
|
||||
"#,
|
||||
)
|
||||
@@ -85,21 +80,32 @@ impl CoconutCredentialManager {
|
||||
///
|
||||
/// * `id`: Database id.
|
||||
/// * `gateway_id`: id of the gateway that received the credential
|
||||
pub async fn consume_coconut_credential(
|
||||
&self,
|
||||
id: i64,
|
||||
gateway_id: &str,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
pub async fn consume_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO credential_usage (credential_id, gateway_id_bs58) VALUES (?, ?)",
|
||||
id,
|
||||
gateway_id
|
||||
"UPDATE ecash_credentials SET consumed = TRUE WHERE id = ?",
|
||||
id
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_issued_credential(
|
||||
&self,
|
||||
credential_data: &[u8],
|
||||
id: i64,
|
||||
consumed: bool,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"UPDATE ecash_credentials SET credential_data = ?, consumed = ? WHERE id = ?",
|
||||
credential_data,
|
||||
consumed,
|
||||
id
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
/// Marks the specified credential as expired
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -107,7 +113,7 @@ impl CoconutCredentialManager {
|
||||
/// * `id`: Id of the credential to mark as expired.
|
||||
pub async fn mark_expired(&self, id: i64) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"UPDATE coconut_credentials SET expired = TRUE WHERE id = ?",
|
||||
"UPDATE ecash_credentials SET expired = TRUE WHERE id = ?",
|
||||
id
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
|
||||
@@ -53,12 +53,11 @@ impl Storage for EphemeralStorage {
|
||||
|
||||
async fn get_next_unspent_credential(
|
||||
&self,
|
||||
gateway_id: &str,
|
||||
) -> Result<Option<StoredIssuedCredential>, Self::StorageError> {
|
||||
// first try to get a free pass if available, otherwise fallback to bandwidth voucher
|
||||
let maybe_freepass = self
|
||||
.coconut_credential_manager
|
||||
.get_next_unspect_freepass(gateway_id)
|
||||
.get_next_unspent_freepass()
|
||||
.await;
|
||||
if maybe_freepass.is_some() {
|
||||
return Ok(maybe_freepass);
|
||||
@@ -66,18 +65,29 @@ impl Storage for EphemeralStorage {
|
||||
|
||||
Ok(self
|
||||
.coconut_credential_manager
|
||||
.get_next_unspect_bandwidth_voucher()
|
||||
.get_next_unspent_ticketbook()
|
||||
.await)
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.consume_coconut_credential(id)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_issued_credential<'a>(
|
||||
&self,
|
||||
bandwidth_credential: StorableIssuedCredential<'a>,
|
||||
id: i64,
|
||||
gateway_id: &str,
|
||||
consumed: bool,
|
||||
) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.consume_coconut_credential(id, gateway_id)
|
||||
.update_issued_credential(bandwidth_credential.credential_data, id, consumed)
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn insert_coin_indices_sig(
|
||||
&self,
|
||||
|
||||
@@ -77,12 +77,11 @@ impl Storage for PersistentStorage {
|
||||
|
||||
async fn get_next_unspent_credential(
|
||||
&self,
|
||||
gateway_id: &str,
|
||||
) -> Result<Option<StoredIssuedCredential>, Self::StorageError> {
|
||||
// first try to get a free pass if available, otherwise fallback to bandwidth voucher
|
||||
let maybe_freepass = self
|
||||
.coconut_credential_manager
|
||||
.get_next_unspect_freepass(gateway_id)
|
||||
.get_next_unspent_freepass()
|
||||
.await?;
|
||||
if maybe_freepass.is_some() {
|
||||
return Ok(maybe_freepass);
|
||||
@@ -90,17 +89,26 @@ impl Storage for PersistentStorage {
|
||||
|
||||
Ok(self
|
||||
.coconut_credential_manager
|
||||
.get_next_unspect_bandwidth_voucher()
|
||||
.get_next_unspent_ticketbook()
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(
|
||||
&self,
|
||||
id: i64,
|
||||
gateway_id: &str,
|
||||
) -> Result<(), StorageError> {
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.consume_coconut_credential(id, gateway_id)
|
||||
.consume_coconut_credential(id)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_issued_credential<'a>(
|
||||
&self,
|
||||
bandwidth_credential: StorableIssuedCredential<'a>,
|
||||
id: i64,
|
||||
consumed: bool,
|
||||
) -> Result<(), Self::StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.update_issued_credential(bandwidth_credential.credential_data, id, consumed)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -19,7 +19,6 @@ pub trait Storage: Send + Sync {
|
||||
/// that is also not marked as expired
|
||||
async fn get_next_unspent_credential(
|
||||
&self,
|
||||
gateway_id: &str,
|
||||
) -> Result<Option<StoredIssuedCredential>, Self::StorageError>;
|
||||
|
||||
/// Marks as consumed in the database the specified credential.
|
||||
@@ -27,10 +26,23 @@ pub trait Storage: Send + Sync {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id`: Id of the credential to be consumed.
|
||||
/// * `gateway_id`: id of the gateway that received the credential.
|
||||
async fn consume_coconut_credential(
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), Self::StorageError>;
|
||||
|
||||
/// Update in the database the specified credential.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `bandwidth_credential` : New credential
|
||||
/// * `id`: Id of the credential to be updated.
|
||||
/// * `consumed`: if the credential is consumed or not
|
||||
///
|
||||
async fn update_issued_credential<'a>(
|
||||
&self,
|
||||
bandwidth_credential: StorableIssuedCredential<'a>,
|
||||
id: i64,
|
||||
consumed: bool,
|
||||
) -> Result<(), Self::StorageError>;
|
||||
|
||||
/// Inserts provided coin_indices_signatures into the database.
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
Reference in New Issue
Block a user