added database code for the serial number storage

This commit is contained in:
Jędrzej Stuczyński
2024-01-26 15:13:25 +00:00
parent 288baea49f
commit ec248ec721
10 changed files with 209 additions and 49 deletions
@@ -53,7 +53,7 @@ impl RecoveryStorage {
pub fn voucher_filename(voucher: &IssuanceBandwidthCredential) -> String {
let prefix = voucher.typ().to_string();
let suffix = voucher.blinded_g1_serial_number_bs58();
let suffix = voucher.blinded_serial_number_bs58();
format!("{prefix}-{suffix}.{DUMPED_VOUCHER_EXTENSION}")
}
+3 -3
View File
@@ -10,9 +10,9 @@ use thiserror::Error;
pub use nym_coconut::{
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar, keygen,
prepare_blind_sign, prove_bandwidth_credential, verify_credential, Attribute, Base58,
BlindSignRequest, BlindedSignature, Bytable, CoconutError, KeyPair, Parameters,
PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare, VerificationKey,
VerifyCredentialRequest,
BlindSignRequest, BlindedSerialNumber, BlindedSignature, Bytable, CoconutError, KeyPair,
Parameters, PrivateAttribute, PublicAttribute, SecretKey, Signature, SignatureShare,
VerificationKey, VerifyCredentialRequest,
};
pub const VOUCHER_INFO_TYPE: &str = "BandwidthVoucher";
@@ -9,10 +9,10 @@ use crate::coconut::bandwidth::{
};
use crate::coconut::utils::scalar_serde_helper;
use crate::error::Error;
use bls12_381::G1Projective;
use nym_credentials_interface::{
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSignature,
Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare, VerificationKey,
aggregate_signature_shares, hash_to_scalar, prepare_blind_sign, Attribute, BlindedSerialNumber,
BlindedSignature, Parameters, PrivateAttribute, PublicAttribute, Signature, SignatureShare,
VerificationKey,
};
use nym_crypto::asymmetric::{encryption, identity};
use nym_validator_client::nym_api::EpochId;
@@ -140,15 +140,14 @@ impl IssuanceBandwidthCredential {
Self::new(FreePassIssuanceData::new(expiry_date))
}
pub fn blind_serial_number_in_g1subgroup(&self) -> G1Projective {
bandwidth_credential_params().gen1() * self.serial_number
pub fn blind_serial_number(&self) -> BlindedSerialNumber {
(bandwidth_credential_params().gen2() * self.serial_number).into()
}
// NOT TO BE CONFUSED WITH BLINDED SERIAL NUMBER IN CREDENTIAL ITSELF
pub fn blinded_g1_serial_number_bs58(&self) -> String {
pub fn blinded_serial_number_bs58(&self) -> String {
use nym_credentials_interface::Base58;
self.blind_serial_number_in_g1subgroup().to_bs58()
self.blind_serial_number().to_bs58()
}
pub fn typ(&self) -> CredentialType {
+1
View File
@@ -24,6 +24,7 @@ pub use scheme::setup::Parameters;
pub use scheme::verification::check_vk_pairing;
pub use scheme::verification::prove_bandwidth_credential;
pub use scheme::verification::verify_credential;
pub use scheme::verification::BlindedSerialNumber;
pub use scheme::verification::VerifyCredentialRequest;
pub use scheme::BlindedSignature;
pub use scheme::Signature;
+39 -10
View File
@@ -1,17 +1,46 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bls12_381::G2Projective;
use group::Curve;
use std::convert::TryFrom;
use std::convert::TryInto;
use crate::error::{CoconutError, Result};
use crate::traits::{Base58, Bytable};
use crate::utils::try_deserialize_g2_projective;
use bls12_381::{G2Affine, G2Projective};
use group::Curve;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
pub struct BlindedSerialNumber {
pub(crate) inner: G2Projective,
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct BlindedSerialNumber(G2Projective);
// use custom Debug implementation to show base58 encoding (rather than raw curve elements)
impl Debug for BlindedSerialNumber {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("BlindedSerialNumber")
.field(&self.to_bs58())
.finish()
}
}
impl From<G2Projective> for BlindedSerialNumber {
fn from(value: G2Projective) -> Self {
BlindedSerialNumber(value)
}
}
impl From<G2Affine> for BlindedSerialNumber {
fn from(value: G2Affine) -> Self {
BlindedSerialNumber(value.into())
}
}
impl Deref for BlindedSerialNumber {
type Target = G2Projective;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TryFrom<&[u8]> for BlindedSerialNumber {
@@ -34,13 +63,13 @@ impl TryFrom<&[u8]> for BlindedSerialNumber {
),
)?;
Ok(BlindedSerialNumber { inner })
Ok(BlindedSerialNumber(inner))
}
}
impl Bytable for BlindedSerialNumber {
fn to_byte_vec(&self) -> Vec<u8> {
self.inner.to_affine().to_compressed().to_vec()
self.0.to_affine().to_compressed().to_vec()
}
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
+16 -25
View File
@@ -1,22 +1,21 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use core::ops::Neg;
use std::convert::TryFrom;
use std::convert::TryInto;
use bls12_381::{multi_miller_loop, G1Affine, G2Prepared, G2Projective, Scalar};
use group::{Curve, Group};
use crate::error::{CoconutError, Result};
use crate::proofs::ProofKappaZeta;
use crate::scheme::double_use::BlindedSerialNumber;
use crate::scheme::setup::Parameters;
use crate::scheme::Signature;
use crate::scheme::VerificationKey;
use crate::traits::{Base58, Bytable};
use crate::utils::try_deserialize_g2_projective;
use crate::Attribute;
use bls12_381::{multi_miller_loop, G1Affine, G2Prepared, G2Projective, Scalar};
use core::ops::Neg;
use group::{Curve, Group};
use std::convert::TryFrom;
use std::convert::TryInto;
pub use crate::scheme::double_use::BlindedSerialNumber;
// TODO NAMING: this whole thing
// Theta
@@ -25,7 +24,7 @@ pub struct VerifyCredentialRequest {
// blinded_message (kappa)
pub blinded_message: G2Projective,
// blinded serial number (zeta)
pub blinded_serial_number: G2Projective,
pub blinded_serial_number: BlindedSerialNumber,
// sigma
pub credential: Signature,
// pi_v
@@ -53,15 +52,10 @@ impl TryFrom<&[u8]> for VerifyCredentialRequest {
),
)?;
// safety: we just checked for the length so the unwraps are fine
#[allow(clippy::unwrap_used)]
let blinded_serial_number_bytes = bytes[96..192].try_into().unwrap();
let blinded_serial_number = try_deserialize_g2_projective(
&blinded_serial_number_bytes,
CoconutError::Deserialization(
"failed to deserialize the blinded serial number (zeta)".to_string(),
),
)?;
let blinded_serial_number_bytes = &bytes[96..192];
let blinded_serial_number =
BlindedSerialNumber::try_from_byte_slice(blinded_serial_number_bytes)?;
let credential = Signature::try_from(&bytes[192..288])?;
let pi_v = ProofKappaZeta::from_bytes(&bytes[288..])?;
@@ -87,7 +81,7 @@ impl VerifyCredentialRequest {
pub fn has_blinded_serial_number(&self, blinded_serial_number_bs58: &str) -> Result<bool> {
let blinded_serial_number = BlindedSerialNumber::try_from_bs58(blinded_serial_number_bs58)?;
let ret = self.blinded_serial_number.eq(&blinded_serial_number.inner);
let ret = self.blinded_serial_number.eq(&blinded_serial_number);
Ok(ret)
}
@@ -112,10 +106,7 @@ impl VerifyCredentialRequest {
}
pub fn blinded_serial_number_bs58(&self) -> String {
let blinded_serial_nuumber = BlindedSerialNumber {
inner: self.blinded_serial_number,
};
blinded_serial_nuumber.to_bs58()
self.blinded_serial_number.to_bs58()
}
}
@@ -198,7 +189,7 @@ pub fn prove_bandwidth_credential(
Ok(VerifyCredentialRequest {
blinded_message,
blinded_serial_number,
blinded_serial_number: blinded_serial_number.into(),
credential: signature_prime,
pi_v,
})
@@ -0,0 +1,11 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
CREATE TABLE spent_credential
(
blinded_serial_number_bs58 TEXT NOT NULL PRIMARY KEY UNIQUE,
was_freepass BOOLEAN NOT NULL,
client_address_bs58 TEXT NOT NULL REFERENCES shared_keys (client_address_bs58)
);
+51 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::storage::models::PersistedBandwidth;
use crate::node::storage::models::{PersistedBandwidth, SpentCredential};
#[derive(Clone)]
pub(crate) struct BandwidthManager {
@@ -103,4 +103,54 @@ impl BandwidthManager {
.await?;
Ok(())
}
/// Mark received credential as spent and insert it into the storage.
///
/// # Arguments
///
/// * `blinded_serial_number_bs58`: the unique blinded serial number embedded in the credential
/// * `was_freepass`: indicates whether the spent credential was a freepass
/// * `client_address_bs58`: address of the client that spent the credential
pub(crate) async fn insert_spent_credential(
&self,
blinded_serial_number_bs58: &str,
was_freepass: bool,
client_address_bs58: &str,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO spent_credential
(blinded_serial_number_bs58, was_freepass, client_address_bs58)
VALUES (?, ?, ?)
"#,
blinded_serial_number_bs58,
was_freepass,
client_address_bs58
)
.execute(&self.connection_pool)
.await?;
Ok(())
}
/// Retrieve the spent credential with the provided blinded serial number from the storage.
///
/// # Arguments
///
/// * `blinded_serial_number_bs58`: the unique blinded serial number embedded in the credential
pub(crate) async fn retrieve_spent_credential(
&self,
blinded_serial_number_bs58: &str,
) -> Result<Option<SpentCredential>, sqlx::Error> {
sqlx::query_as!(
SpentCredential,
r#"
SELECT * FROM spent_credential
WHERE blinded_serial_number_bs58 = ?
LIMIT 1
"#,
blinded_serial_number_bs58,
)
.fetch_optional(&self.connection_pool)
.await
}
}
+67
View File
@@ -8,6 +8,7 @@ use crate::node::storage::models::{PersistedSharedKeys, StoredMessage};
use crate::node::storage::shared_keys::SharedKeysManager;
use async_trait::async_trait;
use log::{debug, error};
use nym_credentials_interface::{Base58, BlindedSerialNumber};
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::DestinationAddressBytes;
use sqlx::ConnectOptions;
@@ -134,6 +135,29 @@ pub(crate) trait Storage: Send + Sync {
client_address: DestinationAddressBytes,
amount: i64,
) -> Result<(), StorageError>;
/// Mark received credential as spent and insert it into the storage.
///
/// # Arguments
///
/// * `blinded_serial_number`: the unique blinded serial number embedded in the credential
/// * `client_address`: address of the client that spent the credential
async fn insert_spent_credential(
&self,
blinded_serial_number: BlindedSerialNumber,
was_freepass: bool,
client_address: DestinationAddressBytes,
) -> Result<(), StorageError>;
/// Check if the credential with the provided blinded serial number if already present in the storage.
///
/// # Arguments
///
/// * `blinded_serial_number`: the unique blinded serial number embedded in the credential
async fn contains_credential(
&self,
blinded_serial_number: &BlindedSerialNumber,
) -> Result<bool, StorageError>;
}
// note that clone here is fine as upon cloning the same underlying pool will be used
@@ -304,6 +328,34 @@ impl Storage for PersistentStorage {
.await?;
Ok(())
}
async fn insert_spent_credential(
&self,
blinded_serial_number: BlindedSerialNumber,
was_freepass: bool,
client_address: DestinationAddressBytes,
) -> Result<(), StorageError> {
self.bandwidth_manager
.insert_spent_credential(
&blinded_serial_number.to_bs58(),
was_freepass,
&client_address.as_base58_string(),
)
.await?;
Ok(())
}
async fn contains_credential(
&self,
blinded_serial_number: &BlindedSerialNumber,
) -> Result<bool, StorageError> {
let cred = self
.bandwidth_manager
.retrieve_spent_credential(&blinded_serial_number.to_bs58())
.await?;
Ok(cred.is_some())
}
}
/// In-memory implementation of `Storage`. The intention is primarily in testing environments.
@@ -393,4 +445,19 @@ impl Storage for InMemStorage {
) -> Result<(), StorageError> {
todo!()
}
async fn insert_spent_credential(
&self,
blinded_serial_number: BlindedSerialNumber,
client_address: DestinationAddressBytes,
) -> Result<(), StorageError> {
todo!()
}
async fn contains_credential(
&self,
blinded_serial_number: &BlindedSerialNumber,
) -> Result<bool, StorageError> {
todo!()
}
}
+13 -1
View File
@@ -1,6 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use sqlx::FromRow;
pub(crate) struct PersistedSharedKeys {
pub(crate) client_address_bs58: String,
pub(crate) derived_aes128_ctr_blake3_hmac_keys_bs58: String,
@@ -18,3 +20,13 @@ pub(crate) struct PersistedBandwidth {
pub(crate) client_address_bs58: String,
pub(crate) available: i64,
}
#[derive(Debug, Clone, FromRow)]
pub(crate) struct SpentCredential {
#[allow(dead_code)]
pub(crate) blinded_serial_number_bs58: String,
#[allow(dead_code)]
pub(crate) was_freepass: bool,
#[allow(dead_code)]
pub(crate) client_address_bs58: String,
}