persisting the issued credentials
This commit is contained in:
Generated
+2
@@ -5343,6 +5343,7 @@ dependencies = [
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5543,6 +5544,7 @@ dependencies = [
|
||||
"sqlx",
|
||||
"subtle-encoding",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BandwidthControllerError;
|
||||
use nym_coconut::Base58;
|
||||
use nym_credential_storage::models::StorableIssuedCredential;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::coconut::bandwidth::{CredentialType, IssuanceBandwidthCredential};
|
||||
use nym_credentials::coconut::utils::obtain_aggregate_signature;
|
||||
@@ -13,6 +13,7 @@ use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use rand::rngs::OsRng;
|
||||
use state::State;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
pub mod state;
|
||||
|
||||
@@ -43,7 +44,7 @@ where
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub async fn get_credential<C, St>(
|
||||
pub async fn get_bandwidth_voucher<C, St>(
|
||||
state: &State,
|
||||
client: &C,
|
||||
storage: &St,
|
||||
@@ -54,7 +55,7 @@ where
|
||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
// temporary
|
||||
assert!(!state.voucher.typ().is_free_pass());
|
||||
assert!(state.voucher.typ().is_voucher());
|
||||
|
||||
let epoch_id = client.get_current_epoch().await?.epoch_id;
|
||||
let threshold = client
|
||||
@@ -66,19 +67,21 @@ where
|
||||
|
||||
let signature =
|
||||
obtain_aggregate_signature(&state.voucher, &coconut_api_clients, threshold).await?;
|
||||
let issued = state.voucher.to_issued_credential(signature);
|
||||
|
||||
// make sure the data gets zeroized after persisting it
|
||||
let credential_data = Zeroizing::new(issued.pack_v1());
|
||||
let storable = StorableIssuedCredential {
|
||||
serialization_revision: issued.current_serialization_revision(),
|
||||
credential_data: credential_data.as_ref(),
|
||||
credential_type: issued.typ().to_string(),
|
||||
epoch_id: epoch_id
|
||||
.try_into()
|
||||
.expect("our epoch is has run over u32::MAX!"),
|
||||
};
|
||||
|
||||
// we asserted the that the bandwidth credential we obtained is **NOT** the free pass
|
||||
// so the first public attribute must be the value
|
||||
let voucher_value = state.voucher.get_plain_public_attributes()[0].clone();
|
||||
storage
|
||||
.insert_coconut_credential(
|
||||
voucher_value,
|
||||
CredentialType::Voucher.to_string(),
|
||||
state.voucher.get_private_attributes()[0].to_bs58(),
|
||||
state.voucher.get_private_attributes()[1].to_bs58(),
|
||||
signature.to_bs58(),
|
||||
epoch_id.to_string(),
|
||||
)
|
||||
.insert_issued_credential(storable)
|
||||
.await
|
||||
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BandwidthControllerError;
|
||||
use crate::utils::stored_credential_to_issued_bandwidth;
|
||||
use log::{error, warn};
|
||||
use nym_credential_storage::error::StorageError;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::coconut::bandwidth::CredentialSpendingData;
|
||||
use nym_credentials::coconut::utils::obtain_aggregate_verification_key;
|
||||
@@ -12,7 +11,6 @@ use nym_credentials_interface::VerificationKey;
|
||||
use nym_validator_client::coconut::all_coconut_api_clients;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub mod acquire;
|
||||
pub mod error;
|
||||
@@ -69,8 +67,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
.await
|
||||
.map_err(|err| BandwidthControllerError::CredentialStorageError(Box::new(err)))?;
|
||||
|
||||
let epoch_id = u64::from_str(&retrieved_credential.epoch_id)
|
||||
.map_err(|_| StorageError::InconsistentData)?;
|
||||
let epoch_id = retrieved_credential.epoch_id as EpochId;
|
||||
let credential_id = retrieved_credential.id;
|
||||
|
||||
let issued_bandwidth = stored_credential_to_issued_bandwidth(retrieved_credential)?;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BandwidthControllerError;
|
||||
use nym_credential_storage::models::StoredIssuedCredential;
|
||||
use nym_credential_storage::models::{StorableIssuedCredential, StoredIssuedCredential};
|
||||
use nym_credentials::coconut::bandwidth::IssuedBandwidthCredential;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ async-trait = { workspace = true }
|
||||
|
||||
log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["sync"]}
|
||||
tokio = { workspace = true, features = ["sync"]}
|
||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
workspace = true
|
||||
|
||||
@@ -3,3 +3,15 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
DROP TABLE coconut_credentials;
|
||||
CREATE TABLE coconut_credentials
|
||||
(
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
-- introduce a way for us to introduce breaking changes in serialization
|
||||
serialization_revision INTEGER NOT NULL,
|
||||
credential_type TEXT NOT NULL,
|
||||
credential_data BLOB NOT NULL,
|
||||
epoch_id TEXT NOT NULL,
|
||||
consumed BOOLEAN NOT NULL
|
||||
);
|
||||
@@ -1,59 +1,60 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::CoconutCredential;
|
||||
use crate::models::StoredIssuedCredential;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CoconutCredentialManager {
|
||||
inner: Arc<RwLock<Vec<CoconutCredential>>>,
|
||||
inner: Arc<RwLock<CoconutCredentialManagerInner>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CoconutCredentialManagerInner {
|
||||
data: Vec<StoredIssuedCredential>,
|
||||
_next_id: i64,
|
||||
}
|
||||
|
||||
impl CoconutCredentialManagerInner {
|
||||
fn next_id(&mut self) -> i64 {
|
||||
let next = self._next_id;
|
||||
self._next_id += 1;
|
||||
next
|
||||
}
|
||||
}
|
||||
|
||||
impl CoconutCredentialManager {
|
||||
/// Creates new empty instance of the `CoconutCredentialManager`.
|
||||
pub fn new() -> Self {
|
||||
CoconutCredentialManager {
|
||||
inner: Arc::new(RwLock::new(Vec::new())),
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts provided signature into the database.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `voucher_value`: Plaintext bandwidth value of the credential.
|
||||
/// * `voucher_info`: Plaintext information of the credential.
|
||||
/// * `serial_number`: Base58 representation of the serial number attribute.
|
||||
/// * `binding_number`: Base58 representation of the binding number attribute.
|
||||
/// * `signature`: Coconut credential in the form of a signature.
|
||||
pub async fn insert_coconut_credential(
|
||||
pub async fn insert_issued_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
credential_type: String,
|
||||
serialization_revision: u8,
|
||||
credential_data: &[u8],
|
||||
epoch_id: u32,
|
||||
) {
|
||||
let mut creds = self.inner.write().await;
|
||||
let id = creds.len() as i64;
|
||||
creds.push(CoconutCredential {
|
||||
let mut inner = self.inner.write().await;
|
||||
let id = inner.next_id();
|
||||
inner.data.push(StoredIssuedCredential {
|
||||
id,
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
serial_number,
|
||||
binding_number,
|
||||
signature,
|
||||
serialization_revision,
|
||||
credential_data: credential_data.to_vec(),
|
||||
credential_type,
|
||||
epoch_id,
|
||||
consumed: false,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
pub async fn get_next_coconut_credential(&self) -> Option<CoconutCredential> {
|
||||
pub async fn get_next_unspent_credential(&self) -> Option<StoredIssuedCredential> {
|
||||
let creds = self.inner.read().await;
|
||||
creds.iter().find(|c| !c.consumed).cloned()
|
||||
creds.data.iter().find(|c| !c.consumed).cloned()
|
||||
}
|
||||
|
||||
/// Consumes in the database the specified credential.
|
||||
@@ -63,7 +64,7 @@ impl CoconutCredentialManager {
|
||||
/// * `id`: Database id.
|
||||
pub async fn consume_coconut_credential(&self, id: i64) {
|
||||
let mut creds = self.inner.write().await;
|
||||
if let Some(cred) = creds.get_mut(id as usize) {
|
||||
if let Some(cred) = creds.data.get_mut(id as usize) {
|
||||
cred.consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::CoconutCredential;
|
||||
use crate::models::StoredIssuedCredential;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CoconutCredentialManager {
|
||||
@@ -18,43 +18,29 @@ impl CoconutCredentialManager {
|
||||
CoconutCredentialManager { connection_pool }
|
||||
}
|
||||
|
||||
/// Inserts provided signature into the database.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `voucher_value`: Plaintext bandwidth value of the credential.
|
||||
/// * `voucher_info`: Plaintext information of the credential.
|
||||
/// * `serial_number`: Base58 representation of the serial number attribute.
|
||||
/// * `binding_number`: Base58 representation of the binding number attribute.
|
||||
/// * `signature`: Coconut credential in the form of a signature.
|
||||
pub async fn insert_coconut_credential(
|
||||
pub async fn insert_issued_credential(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
credential_type: String,
|
||||
serialization_revision: u8,
|
||||
credential_data: &[u8],
|
||||
epoch_id: u32,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature, epoch_id, consumed) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
voucher_value, voucher_info, serial_number, binding_number, signature, epoch_id, false
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
r#"
|
||||
INSERT INTO coconut_credentials(serialization_revision, credential_type, credential_data, epoch_id, consumed)
|
||||
VALUES (?, ?, ?, ?, false)
|
||||
"#,
|
||||
serialization_revision, credential_type, credential_data, epoch_id
|
||||
).execute(&self.connection_pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
pub async fn get_next_coconut_credential(
|
||||
pub async fn get_next_unspent_credential(
|
||||
&self,
|
||||
) -> Result<Option<CoconutCredential>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
CoconutCredential,
|
||||
"SELECT * FROM coconut_credentials WHERE NOT consumed"
|
||||
)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
) -> Result<Option<StoredIssuedCredential>, sqlx::Error> {
|
||||
sqlx::query_as("SELECT * FROM coconut_credentials WHERE NOT consumed LIMIT 1")
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Consumes in the database the specified credential.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::backends::memory::CoconutCredentialManager;
|
||||
use crate::error::StorageError;
|
||||
use crate::models::{CoconutCredential, StoredIssuedCredential};
|
||||
use crate::models::{StorableIssuedCredential, StoredIssuedCredential};
|
||||
use crate::storage::Storage;
|
||||
use async_trait::async_trait;
|
||||
|
||||
@@ -27,43 +27,31 @@ impl Default for EphemeralStorage {
|
||||
impl Storage for EphemeralStorage {
|
||||
type StorageError = StorageError;
|
||||
|
||||
async fn insert_coconut_credential(
|
||||
async fn insert_issued_credential<'a>(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
bandwidth_credential: StorableIssuedCredential<'a>,
|
||||
) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.insert_coconut_credential(
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
serial_number,
|
||||
binding_number,
|
||||
signature,
|
||||
epoch_id,
|
||||
.insert_issued_credential(
|
||||
bandwidth_credential.credential_type,
|
||||
bandwidth_credential.serialization_revision,
|
||||
bandwidth_credential.credential_data,
|
||||
bandwidth_credential.epoch_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||
let credential = self
|
||||
.coconut_credential_manager
|
||||
.get_next_coconut_credential()
|
||||
.await
|
||||
.ok_or(StorageError::NoCredential)?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
async fn get_next_unspent_credential(
|
||||
&self,
|
||||
) -> Result<StoredIssuedCredential, Self::StorageError> {
|
||||
todo!()
|
||||
let credential = self
|
||||
.coconut_credential_manager
|
||||
.get_next_unspent_credential()
|
||||
.await
|
||||
.ok_or(StorageError::NoCredential)?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
|
||||
|
||||
@@ -1,31 +1,38 @@
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CoconutCredential {
|
||||
#[allow(dead_code)]
|
||||
pub id: i64,
|
||||
pub voucher_value: String,
|
||||
pub voucher_info: String,
|
||||
pub serial_number: String,
|
||||
pub binding_number: String,
|
||||
pub signature: String,
|
||||
pub epoch_id: String,
|
||||
pub consumed: bool,
|
||||
}
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
// #[derive(Clone)]
|
||||
// pub struct CoconutCredential {
|
||||
// #[allow(dead_code)]
|
||||
// pub id: i64,
|
||||
// pub voucher_value: String,
|
||||
// pub voucher_info: String,
|
||||
// pub serial_number: String,
|
||||
// pub binding_number: String,
|
||||
// pub signature: String,
|
||||
// pub epoch_id: String,
|
||||
// pub consumed: bool,
|
||||
// }
|
||||
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
|
||||
#[derive(Zeroize, ZeroizeOnDrop, Clone)]
|
||||
pub struct StoredIssuedCredential {
|
||||
pub id: i64,
|
||||
|
||||
pub serial_number: String,
|
||||
pub binding_number: String,
|
||||
pub serialization_revision: u8,
|
||||
pub credential_data: Vec<u8>,
|
||||
pub credential_type: String,
|
||||
|
||||
pub signature: String,
|
||||
|
||||
pub variant_type: String,
|
||||
pub serialized_variant_data: String,
|
||||
|
||||
pub epoch_id: String,
|
||||
pub epoch_id: u32,
|
||||
pub consumed: bool,
|
||||
}
|
||||
|
||||
pub struct StorableIssuedCredential<'a> {
|
||||
pub serialization_revision: u8,
|
||||
pub credential_data: &'a [u8],
|
||||
pub credential_type: String,
|
||||
|
||||
pub epoch_id: u32,
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::backends::sqlite::CoconutCredentialManager;
|
||||
use crate::error::StorageError;
|
||||
use crate::storage::Storage;
|
||||
|
||||
use crate::models::{CoconutCredential, StoredIssuedCredential};
|
||||
use crate::models::{StorableIssuedCredential, StoredIssuedCredential};
|
||||
use async_trait::async_trait;
|
||||
use log::{debug, error};
|
||||
use sqlx::ConnectOptions;
|
||||
@@ -58,45 +58,34 @@ impl PersistentStorage {
|
||||
impl Storage for PersistentStorage {
|
||||
type StorageError = StorageError;
|
||||
|
||||
async fn insert_coconut_credential(
|
||||
async fn insert_issued_credential<'a>(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
) -> Result<(), StorageError> {
|
||||
bandwidth_credential: StorableIssuedCredential<'a>,
|
||||
) -> Result<(), Self::StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.insert_coconut_credential(
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
serial_number,
|
||||
binding_number,
|
||||
signature,
|
||||
epoch_id,
|
||||
.insert_issued_credential(
|
||||
bandwidth_credential.credential_type,
|
||||
bandwidth_credential.serialization_revision,
|
||||
bandwidth_credential.credential_data,
|
||||
bandwidth_credential.epoch_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||
async fn get_next_unspent_credential(
|
||||
&self,
|
||||
) -> Result<StoredIssuedCredential, Self::StorageError> {
|
||||
let credential = self
|
||||
.coconut_credential_manager
|
||||
.get_next_coconut_credential()
|
||||
.get_next_unspent_credential()
|
||||
.await?
|
||||
.ok_or(StorageError::NoCredential)?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
async fn get_next_unspent_credential(
|
||||
&self,
|
||||
) -> Result<StoredIssuedCredential, Self::StorageError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn consume_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
|
||||
self.coconut_credential_manager
|
||||
.consume_coconut_credential(id)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::{CoconutCredential, StoredIssuedCredential};
|
||||
use crate::models::{StorableIssuedCredential, StoredIssuedCredential};
|
||||
use async_trait::async_trait;
|
||||
use std::error::Error;
|
||||
|
||||
@@ -9,31 +9,11 @@ use std::error::Error;
|
||||
pub trait Storage: Send + Sync {
|
||||
type StorageError: Error;
|
||||
|
||||
/// Inserts provided signature into the database.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `voucher_value`: How much bandwidth is in the credential.
|
||||
/// * `voucher_info`: What type of credential it is.
|
||||
/// * `serial_number`: Serial number of the credential.
|
||||
/// * `binding_number`: Binding number of the credential.
|
||||
/// * `signature`: Coconut credential in the form of a signature.
|
||||
/// * `epoch_id`: The epoch when it was signed.
|
||||
#[deprecated]
|
||||
async fn insert_coconut_credential(
|
||||
async fn insert_issued_credential<'a>(
|
||||
&self,
|
||||
voucher_value: String,
|
||||
voucher_info: String,
|
||||
serial_number: String,
|
||||
binding_number: String,
|
||||
signature: String,
|
||||
epoch_id: String,
|
||||
bandwidth_credential: StorableIssuedCredential<'a>,
|
||||
) -> Result<(), Self::StorageError>;
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
#[deprecated]
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, Self::StorageError>;
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
async fn get_next_unspent_credential(
|
||||
&self,
|
||||
|
||||
@@ -44,7 +44,7 @@ where
|
||||
|
||||
let state = nym_bandwidth_controller::acquire::deposit(client, amount.clone()).await?;
|
||||
|
||||
if nym_bandwidth_controller::acquire::get_credential(&state, client, persistent_storage)
|
||||
if nym_bandwidth_controller::acquire::get_bandwidth_voucher(&state, client, persistent_storage)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
@@ -142,7 +142,8 @@ where
|
||||
let state = State::new(voucher);
|
||||
|
||||
if let Err(e) =
|
||||
nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await
|
||||
nym_bandwidth_controller::acquire::get_bandwidth_voucher(&state, client, shared_storage)
|
||||
.await
|
||||
{
|
||||
error!("Could not recover deposit {voucher_name} due to {e}, try again later",)
|
||||
} else {
|
||||
|
||||
@@ -13,6 +13,7 @@ cosmrs = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
time = { workspace = true, features = ["serde"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
# I guess temporarily until we get serde support in coconut up and running
|
||||
@@ -20,7 +21,6 @@ nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "serde"] }
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.7.3"
|
||||
|
||||
@@ -30,7 +30,7 @@ impl FreePassIssuedData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
|
||||
#[derive(Zeroize, Serialize, Deserialize)]
|
||||
pub struct FreePassIssuanceData {
|
||||
/// the plain validity value of this credential expressed as unix timestamp
|
||||
#[zeroize(skip)]
|
||||
|
||||
@@ -238,9 +238,17 @@ impl IssuanceBandwidthCredential {
|
||||
.map_err(Error::SignatureAggregationError)
|
||||
}
|
||||
|
||||
// also drops self after the conversion
|
||||
pub fn into_issued_credential(
|
||||
self,
|
||||
aggregate_signature: Signature,
|
||||
) -> IssuedBandwidthCredential {
|
||||
self.to_issued_credential(aggregate_signature)
|
||||
}
|
||||
|
||||
pub fn to_issued_credential(
|
||||
&self,
|
||||
aggregate_signature: Signature,
|
||||
) -> IssuedBandwidthCredential {
|
||||
IssuedBandwidthCredential::new(
|
||||
self.serial_number,
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::coconut::bandwidth::bandwidth_credential_params;
|
||||
use crate::coconut::bandwidth::freepass::FreePassIssuedData;
|
||||
use crate::coconut::bandwidth::issuance::{
|
||||
BandwidthCredentialIssuanceDataVariant, IssuanceBandwidthCredential,
|
||||
};
|
||||
use crate::coconut::bandwidth::voucher::BandwidthVoucherIssuedData;
|
||||
use crate::coconut::bandwidth::{
|
||||
bandwidth_credential_params, CredentialSpendingData, CredentialType,
|
||||
};
|
||||
use crate::coconut::bandwidth::{CredentialSpendingData, CredentialType};
|
||||
use crate::coconut::utils::scalar_serde_helper;
|
||||
use crate::error::Error;
|
||||
use nym_credentials_interface::prove_bandwidth_credential;
|
||||
use nym_credentials_interface::{
|
||||
prove_bandwidth_credential, Parameters, PrivateAttribute, PublicAttribute, Signature,
|
||||
VerificationKey,
|
||||
Parameters, PrivateAttribute, PublicAttribute, Signature, VerificationKey,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
|
||||
pub const CURRENT_SERIALIZATION_REVISION: u8 = 1;
|
||||
|
||||
#[derive(Zeroize, Serialize, Deserialize)]
|
||||
pub enum BandwidthCredentialIssuedDataVariant {
|
||||
Voucher(BandwidthVoucherIssuedData),
|
||||
FreePass(FreePassIssuedData),
|
||||
@@ -68,13 +70,15 @@ impl BandwidthCredentialIssuedDataVariant {
|
||||
}
|
||||
|
||||
// the only important thing to zeroize here are the private attributes, the rest can be made fully public for what we're concerned
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
#[derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
|
||||
pub struct IssuedBandwidthCredential {
|
||||
// private attributes
|
||||
/// a random secret value generated by the client used for double-spending detection
|
||||
#[serde(with = "scalar_serde_helper")]
|
||||
serial_number: PrivateAttribute,
|
||||
|
||||
/// a random secret value generated by the client used to bind multiple credentials together
|
||||
#[serde(with = "scalar_serde_helper")]
|
||||
binding_number: PrivateAttribute,
|
||||
|
||||
/// the underlying aggregated signature on the attributes
|
||||
@@ -85,6 +89,7 @@ pub struct IssuedBandwidthCredential {
|
||||
variant_data: BandwidthCredentialIssuedDataVariant,
|
||||
|
||||
/// type of the bandwdith credential hashed onto a scalar
|
||||
#[serde(with = "scalar_serde_helper")]
|
||||
type_prehashed: PublicAttribute,
|
||||
}
|
||||
|
||||
@@ -105,6 +110,28 @@ impl IssuedBandwidthCredential {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_serialization_revision(&self) -> u8 {
|
||||
CURRENT_SERIALIZATION_REVISION
|
||||
}
|
||||
|
||||
/// Pack (serialize) this credential data into a stream of bytes using v1 serializer.
|
||||
pub fn pack_v1(&self) -> Vec<u8> {
|
||||
use bincode::Options;
|
||||
// safety: our data format is stable and thus the serialization should not fail
|
||||
make_storable_bincode_serializer().serialize(self).unwrap()
|
||||
}
|
||||
|
||||
/// Unpack (deserialize) the credential data from the given bytes using v1 serializer.
|
||||
pub fn unpack_v1(bytes: &[u8]) -> Result<Self, Error> {
|
||||
use bincode::Options;
|
||||
Ok(make_storable_bincode_serializer().deserialize(bytes)?)
|
||||
}
|
||||
|
||||
pub fn randomise_signature(&mut self) {
|
||||
let signature_prime = self.signature.randomise(bandwidth_credential_params());
|
||||
self.signature = signature_prime.0
|
||||
}
|
||||
|
||||
pub fn default_parameters() -> Parameters {
|
||||
IssuanceBandwidthCredential::default_parameters()
|
||||
}
|
||||
@@ -142,3 +169,10 @@ impl IssuedBandwidthCredential {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn make_storable_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
bincode::DefaultOptions::new()
|
||||
.with_big_endian()
|
||||
.with_varint_encoding()
|
||||
}
|
||||
|
||||
Generated
+1
@@ -3915,6 +3915,7 @@ dependencies = [
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -57,7 +57,7 @@ where
|
||||
pub async fn acquire(&self, amount: u128) -> Result<()> {
|
||||
let amount = Coin::new(amount, &self.network_details.chain_details.mix_denom.base);
|
||||
let state = nym_bandwidth_controller::acquire::deposit(&self.client, amount).await?;
|
||||
nym_bandwidth_controller::acquire::get_credential(&state, &self.client, self.storage)
|
||||
nym_bandwidth_controller::acquire::get_bandwidth_voucher(&state, &self.client, self.storage)
|
||||
.await
|
||||
.map_err(|reason| Error::UnconvertedDeposit {
|
||||
reason,
|
||||
@@ -71,8 +71,12 @@ where
|
||||
let voucher = IssuanceBandwidthCredential::try_from_recovered_bytes(voucher_blob)
|
||||
.map_err(|_| Error::InvalidVoucherBlob)?;
|
||||
let state = State::new(voucher);
|
||||
nym_bandwidth_controller::acquire::get_credential(&state, &self.client, self.storage)
|
||||
.await?;
|
||||
nym_bandwidth_controller::acquire::get_bandwidth_voucher(
|
||||
&state,
|
||||
&self.client,
|
||||
self.storage,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ pub use nym_client_core::{
|
||||
config::{GatewayEndpointConfig, GroupBy},
|
||||
};
|
||||
pub use nym_credential_storage::{
|
||||
ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage, models::CoconutCredential,
|
||||
storage::Storage as CredentialStorage,
|
||||
ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage,
|
||||
models::StoredIssuedCredential, storage::Storage as CredentialStorage,
|
||||
};
|
||||
pub use nym_network_defaults::NymNetworkDetails;
|
||||
pub use nym_socks5_client_core::config::Socks5;
|
||||
|
||||
Reference in New Issue
Block a user