diff --git a/common/credential-storage/src/backends/memory.rs b/common/credential-storage/src/backends/memory.rs index d796876365..63024d19cd 100644 --- a/common/credential-storage/src/backends/memory.rs +++ b/common/credential-storage/src/backends/memory.rs @@ -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, - credential_usage: Vec, _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 { + pub async fn get_next_unspent_ticketbook(&self) -> Option { 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 { + pub async fn get_next_unspent_freepass(&self) -> Option { 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 diff --git a/common/credential-storage/src/backends/sqlite.rs b/common/credential-storage/src/backends/sqlite.rs index a3ffc901b9..23a9445b4d 100644 --- a/common/credential-storage/src/backends/sqlite.rs +++ b/common/credential-storage/src/backends/sqlite.rs @@ -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, 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, 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) diff --git a/common/credential-storage/src/ephemeral_storage.rs b/common/credential-storage/src/ephemeral_storage.rs index 09ffaac304..286901aa29 100644 --- a/common/credential-storage/src/ephemeral_storage.rs +++ b/common/credential-storage/src/ephemeral_storage.rs @@ -53,12 +53,11 @@ impl Storage for EphemeralStorage { async fn get_next_unspent_credential( &self, - gateway_id: &str, ) -> Result, 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, diff --git a/common/credential-storage/src/persistent_storage.rs b/common/credential-storage/src/persistent_storage.rs index 88dff50e7d..79311f7b1f 100644 --- a/common/credential-storage/src/persistent_storage.rs +++ b/common/credential-storage/src/persistent_storage.rs @@ -77,12 +77,11 @@ impl Storage for PersistentStorage { async fn get_next_unspent_credential( &self, - gateway_id: &str, ) -> Result, 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(()) diff --git a/common/credential-storage/src/storage.rs b/common/credential-storage/src/storage.rs index be5aa57e1c..03b61ca7b5 100644 --- a/common/credential-storage/src/storage.rs +++ b/common/credential-storage/src/storage.rs @@ -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, 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