wip in removing the Credential type for more strongly typed alternative

This commit is contained in:
Jędrzej Stuczyński
2024-02-07 16:06:13 +00:00
parent b50468b566
commit 67ce8a98a5
17 changed files with 209 additions and 70 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
Ok(obtain_aggregate_verification_key(&coconut_api_clients).await?)
}
pub async fn prepare_coconut_credential(
pub async fn prepare_bandwidth_credential(
&self,
) -> Result<PreparedCredential, BandwidthControllerError>
where
@@ -574,7 +574,7 @@ impl<C, St> GatewayClient<C, St> {
.bandwidth_controller
.as_ref()
.unwrap()
.prepare_coconut_credential()
.prepare_bandwidth_credential()
.await?;
self.claim_coconut_bandwidth(prepared_credential.data, prepared_credential.epoch_id)
+1 -1
View File
@@ -11,4 +11,4 @@ getset = "0.1.1"
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
nym-coconut = {path = "../nymcoconut" }
nym-coconut = { path = "../nymcoconut" }
+1
View File
@@ -12,6 +12,7 @@ thiserror = { workspace = true }
tokio = { workspace = true }
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-coconut = { path = "../nymcoconut" }
nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-validator-client = { path = "../../common/client-libs/validator-client" }
+2 -2
View File
@@ -17,10 +17,10 @@ zeroize = { workspace = true }
# I guess temporarily until we get serde support in coconut up and running
nym-coconut-interface = { path = "../coconut-interface" }
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric"] }
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 = { version = "1.0.189", features = ["derive"] }
serde = { workspace = true, features = ["derive"] }
[dev-dependencies]
rand = "0.7.3"
@@ -27,7 +27,7 @@ pub fn bandwidth_credential_params() -> &'static Parameters {
BANDWIDTH_CREDENTIAL_PARAMS.get_or_init(IssuanceBandwidthCredential::default_parameters)
}
#[derive(Zeroize, ZeroizeOnDrop, Clone, Debug, Serialize, Deserialize)]
#[derive(Zeroize, Copy, Clone, Debug, Serialize, Deserialize)]
pub enum CredentialType {
Voucher,
FreePass,
@@ -37,6 +37,10 @@ impl CredentialType {
pub fn is_free_pass(&self) -> bool {
matches!(self, CredentialType::FreePass)
}
pub fn is_voucher(&self) -> bool {
matches!(self, CredentialType::Voucher)
}
}
impl Display for CredentialType {
@@ -50,24 +54,24 @@ impl Display for CredentialType {
#[derive(Debug, Clone)]
pub struct CredentialSigningData {
pub(crate) pedersen_commitments_openings: Vec<Scalar>,
pub pedersen_commitments_openings: Vec<Scalar>,
pub(crate) blind_sign_request: BlindSignRequest,
pub blind_sign_request: BlindSignRequest,
pub(crate) public_attributes_plain: Vec<String>,
pub public_attributes_plain: Vec<String>,
pub(crate) typ: CredentialType,
pub typ: CredentialType,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CredentialSpendingData {
pub(crate) embedded_private_attributes: usize,
pub embedded_private_attributes: usize,
pub(crate) verify_credential_request: VerifyCredentialRequest,
pub verify_credential_request: VerifyCredentialRequest,
pub(crate) public_attributes_plain: Vec<String>,
pub public_attributes_plain: Vec<String>,
pub(crate) typ: CredentialType,
pub typ: CredentialType,
}
impl CredentialSpendingData {
@@ -90,4 +94,21 @@ impl CredentialSpendingData {
&public_attributes,
)
}
pub fn validate_type_attribute(&self) -> bool {
// the first attribute is variant specific bandwidth encoding, the second one should be the type
let Some(type_plain) = self.public_attributes_plain.get(1) else {
return false;
};
match self.typ {
CredentialType::Voucher => type_plain == VOUCHER_INFO_TYPE,
CredentialType::FreePass => type_plain == FREE_PASS_INFO_TYPE,
}
}
pub fn get_bandwidth_attribute(&self) -> Option<&String> {
// the first attribute is variant specific bandwidth encoding, the second one should be the type
self.public_attributes_plain.first()
}
}
+1 -5
View File
@@ -2,9 +2,5 @@
// SPDX-License-Identifier: Apache-2.0
pub trait NymCredential {
fn prove_credential(&self);
// pub attr
// hashed
// private
fn prove_credential(&self) -> Result<(), ()>;
}
+1 -1
View File
@@ -9,7 +9,7 @@ pub use types::*;
pub mod authentication;
pub mod iv;
mod models;
pub mod models;
pub mod registration;
pub mod types;
+18
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::GatewayRequestsError;
use nym_coconut_interface::CoconutError;
use nym_credentials::coconut::bandwidth::CredentialSpendingData;
use serde::{Deserialize, Serialize};
@@ -20,6 +21,23 @@ impl CredentialSpendingWithEpoch {
CredentialSpendingWithEpoch { data, epoch_id }
}
pub fn matches_blinded_serial_number(
&self,
blinded_serial_number_bs58: &str,
) -> Result<bool, CoconutError> {
self.data
.verify_credential_request
.has_blinded_serial_number(blinded_serial_number_bs58)
}
pub fn unchecked_voucher_value(&self) -> u64 {
self.data
.get_bandwidth_attribute()
.expect("failed to extract bandwidth attribute")
.parse()
.expect("failed to parse voucher value")
}
pub fn to_bytes(&self) -> Vec<u8> {
todo!()
}
+37 -1
View File
@@ -1,13 +1,49 @@
// 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 log::error;
use nym_coconut_interface::Credential;
use nym_credentials::coconut::bandwidth::CredentialType;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BandwidthError {}
pub struct Bandwidth {
value: u64,
}
impl Bandwidth {
pub const fn new(value: u64) -> Bandwidth {
Bandwidth { value }
}
pub fn try_from_raw_value(value: &String, typ: CredentialType) -> Result<Self, BandwidthError> {
// let bandwidth_value = match credential.data.typ {
// CredentialType::Voucher => {
// todo!()
// }
// CredentialType::FreePass => {
// error!("unimplemented handling of free pass credential");
// return Err(());
// }
// };
/*
if bandwidth_value > i64::MAX as u64 {
// note that this would have represented more than 1 exabyte,
// which is like 125,000 worth of hard drives so I don't think we have
// to worry about it for now...
warn!("Somehow we received bandwidth value higher than 9223372036854775807. We don't really want to deal with this now");
return Err(RequestHandlingError::UnsupportedBandwidthValue(
bandwidth_value,
));
}
*/
todo!()
}
pub fn value(&self) -> u64 {
self.value
}
+4 -2
View File
@@ -1,9 +1,11 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::bandwidth::Bandwidth;
pub(crate) mod active_clients;
mod bandwidth;
pub(crate) mod embedded_network_requester;
pub(crate) mod websocket;
pub(crate) const FREE_TESTNET_BANDWIDTH_VALUE: i64 = 64 * 1024 * 1024 * 1024; // 64GB
pub(crate) const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new(64 * 1024 * 1024 * 1024); // 64GB
@@ -19,8 +19,11 @@ use thiserror::Error;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
use nym_coconut_interface::CoconutError;
use nym_credentials::coconut::bandwidth::CredentialType;
use std::{convert::TryFrom, process, time::Duration};
use crate::node::client_handling::bandwidth::BandwidthError;
use crate::node::{
client_handling::{
bandwidth::Bandwidth,
@@ -76,11 +79,20 @@ pub(crate) enum RequestHandlingError {
#[error("Coconut interface error - {0}")]
CoconutInterfaceError(#[from] nym_coconut_interface::error::CoconutInterfaceError),
#[error("coconut failure: {0}")]
CoconutError(#[from] CoconutError),
#[error("coconut api query failure: {0}")]
CoconutApiError(#[from] CoconutApiError),
#[error("Credential error - {0}")]
CredentialError(#[from] nym_credentials::error::Error),
#[error("failed to recover bandwidth value: {0}")]
BandwidthRecoveryFailure(#[from] BandwidthError),
#[error("free pass credentials haven't been implemented yet")]
UnimplementedFreePass,
}
impl RequestHandlingError {
@@ -177,10 +189,10 @@ where
/// # Arguments
///
/// * `amount`: amount to increase the available bandwidth by.
async fn increase_bandwidth(&self, amount: i64) -> Result<(), RequestHandlingError> {
async fn increase_bandwidth(&self, bandwidth: Bandwidth) -> Result<(), RequestHandlingError> {
self.inner
.storage
.increase_bandwidth(self.client.address, amount)
.increase_bandwidth(self.client.address, bandwidth.value() as i64)
.await?;
Ok(())
}
@@ -232,40 +244,45 @@ where
let aggregated_verification_key = self
.inner
.coconut_verifier
.verification_key(*credential.epoch_id())
.verification_key(credential.epoch_id)
.await?;
if !credential.verify(&aggregated_verification_key) {
if !credential.data.validate_type_attribute() {
unimplemented!()
}
let Some(bandwidth_attribute) = credential.data.get_bandwidth_attribute() else {
unimplemented!()
};
let bandwidth = Bandwidth::try_from_raw_value(bandwidth_attribute, credential.data.typ)?;
if !credential.data.verify(&aggregated_verification_key) {
return Err(RequestHandlingError::InvalidBandwidthCredential(
String::from("credential failed to verify on gateway"),
));
}
let api_clients = self
.inner
.coconut_verifier
.api_clients(*credential.epoch_id())
.await?;
match credential.data.typ {
CredentialType::Voucher => {
let api_clients = self
.inner
.coconut_verifier
.api_clients(credential.epoch_id)
.await?;
self.inner
.coconut_verifier
.release_funds(&api_clients, &credential)
.await?;
let bandwidth = Bandwidth::from(credential);
let bandwidth_value = bandwidth.value();
if bandwidth_value > i64::MAX as u64 {
// note that this would have represented more than 1 exabyte,
// which is like 125,000 worth of hard drives so I don't think we have
// to worry about it for now...
warn!("Somehow we received bandwidth value higher than 9223372036854775807. We don't really want to deal with this now");
return Err(RequestHandlingError::UnsupportedBandwidthValue(
bandwidth_value,
));
self.inner
.coconut_verifier
.release_bandwidth_voucher_funds(&api_clients, credential)
.await?;
}
CredentialType::FreePass => {
error!("unimplemented handling of free pass credential");
return Err(RequestHandlingError::UnimplementedFreePass);
}
}
self.increase_bandwidth(bandwidth_value as i64).await?;
self.increase_bandwidth(bandwidth).await?;
let available_total = self.get_available_bandwidth().await?;
Ok(ServerResponse::Bandwidth { available_total })
@@ -4,6 +4,7 @@
use super::authenticated::RequestHandlingError;
use log::*;
use nym_coconut_interface::{Credential, VerificationKey};
use nym_gateway_requests::models::CredentialSpendingWithEpoch;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::MultisigQueryClient;
@@ -151,21 +152,31 @@ impl CoconutVerifier {
Ok(all_coconut_api_clients(self.nyxd_client.read().await.deref(), epoch_id).await?)
}
pub async fn release_funds(
pub async fn release_bandwidth_voucher_funds(
&self,
api_clients: &[CoconutApiClient],
credential: &Credential,
credential: CredentialSpendingWithEpoch,
) -> Result<(), RequestHandlingError> {
if !credential.data.typ.is_voucher() {
unimplemented!()
}
// safety: the voucher funds are released after the credential has already been verified locally
// and the underlying bandwidth value has been extracted, so the below MUST succeed
let voucher_amount = credential.unchecked_voucher_value() as u128;
let blinded_serial_number = credential
.data
.verify_credential_request
.blinded_serial_number_bs58();
let res = self
.nyxd_client
.write()
.await
.spend_credential(
Coin::new(
credential.voucher_value().into(),
self.mix_denom_base.clone(),
),
credential.blinded_serial_number(),
Coin::new(voucher_amount, &self.mix_denom_base),
blinded_serial_number,
self.address.to_string(),
None,
)
@@ -186,27 +197,28 @@ impl CoconutVerifier {
.await
.query_proposal(proposal_id)
.await?;
if !credential.has_blinded_serial_number(&proposal.description)? {
if !credential.matches_blinded_serial_number(&proposal.description)? {
return Err(RequestHandlingError::ProposalIdError {
reason: String::from("proposal has different serial number"),
});
}
let req = nym_api_requests::coconut::VerifyCredentialBody::new(
credential.clone(),
credential,
proposal_id,
self.address.clone(),
);
for client in api_clients {
let ret = client.api_client.verify_bandwidth_credential(&req).await;
let client_url = client.api_client.nym_api.current_url();
match ret {
Ok(res) => {
if !res.verification_result {
debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.api_client.nym_api.current_url());
warn!("Validator at {client_url} didn't accept the credential. It will probably vote No on the spending proposal");
}
}
Err(e) => {
warn!("Validator {} could not be reached. There might be a problem with the coconut endpoint - {:?}", client.api_client.nym_api.current_url(), e);
Err(err) => {
warn!("Validator at {client_url} could not be reached. There might be a problem with the coconut endpoint: {err}");
}
}
}
+2
View File
@@ -16,7 +16,9 @@ serde = { workspace = true, features = ["derive"] }
ts-rs = { workspace = true, optional = true }
tendermint = { workspace = true }
nym-coconut = { path = "../../common/nymcoconut" }
nym-coconut-interface = { path = "../../common/coconut-interface" }
#nym-credentials = { path = "../../common/credentials" }
nym-crypto = { path = "../../common/crypto", features = ["serde", "asymmetric"]}
nym-mixnet-contract-common = { path= "../../common/cosmwasm-smart-contracts/mixnet-contract" }
+18 -9
View File
@@ -1,11 +1,11 @@
// 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::coconut::helpers::issued_credential_plaintext;
use cosmrs::AccountId;
use nym_coconut_interface::{
error::CoconutInterfaceError, hash_to_scalar, Attribute, BlindSignRequest, BlindedSignature,
Bytable, Credential, VerificationKey,
use nym_coconut::{
hash_to_scalar, Attribute, BlindSignRequest, BlindedSignature, Bytable, CoconutError,
Signature, VerificationKey,
};
use nym_crypto::asymmetric::identity;
use serde::{Deserialize, Serialize};
@@ -14,21 +14,30 @@ use tendermint::hash::Hash;
#[derive(Serialize, Deserialize)]
pub struct VerifyCredentialBody {
pub credential: Credential,
/// The cryptographic material required for spending the underlying credential.
pub credential_data: (),
/// The (DKG) epoch id under which the credential has been issued so that the verifier
/// could use correct verification key for validation.
pub epoch_id: u64,
/// Multisig proposal for releasing funds for the provided bandwidth credential
pub proposal_id: u64,
/// Cosmos address of the spender of the credential
pub gateway_cosmos_addr: AccountId,
}
impl VerifyCredentialBody {
pub fn new(
credential: Credential,
credential_data: (),
epoch_id: u64,
proposal_id: u64,
gateway_cosmos_addr: AccountId,
) -> VerifyCredentialBody {
VerifyCredentialBody {
credential,
credential_data,
epoch_id,
proposal_id,
gateway_cosmos_addr,
}
@@ -115,7 +124,7 @@ impl BlindedSignatureResponse {
bs58::encode(&self.to_bytes()).into_string()
}
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, CoconutInterfaceError> {
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, CoconutError> {
let bytes = bs58::decode(val).into_vec()?;
Self::from_bytes(&bytes)
}
@@ -124,7 +133,7 @@ impl BlindedSignatureResponse {
self.blinded_signature.to_byte_vec()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CoconutInterfaceError> {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CoconutError> {
Ok(BlindedSignatureResponse {
blinded_signature: BlindedSignature::from_bytes(bytes)?,
})
+16
View File
@@ -958,6 +958,12 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "const-str"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6"
[[package]]
name = "constant_time_eq"
version = "0.3.0"
@@ -3688,6 +3694,7 @@ dependencies = [
"cosmrs",
"cosmwasm-std",
"getset",
"nym-coconut",
"nym-coconut-interface",
"nym-crypto",
"nym-mixnet-contract-common",
@@ -3723,6 +3730,7 @@ dependencies = [
"clap",
"clap_complete",
"clap_complete_fig",
"const-str",
"log",
"pretty_env_logger",
"schemars",
@@ -3763,6 +3771,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.10.8",
"si-scale",
"sqlx",
"tap",
"thiserror",
@@ -4142,6 +4151,7 @@ dependencies = [
"cfg-if",
"dotenvy",
"hex-literal",
"log",
"once_cell",
"schemars",
"serde",
@@ -6225,6 +6235,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "si-scale"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44beb68bf488343b13ddbd74d1d5d5e6559a58b6dfaee74eb8d5ed4f7ed7666f"
[[package]]
name = "signal-hook"
version = "0.3.17"
+9
View File
@@ -750,6 +750,12 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "const-str"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aca749d3d3f5b87a0d6100509879f9cf486ab510803a4a4e1001da1ff61c2bd6"
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -3092,6 +3098,7 @@ dependencies = [
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
"cosmwasm-std",
"getset",
"nym-coconut",
"nym-coconut-interface",
"nym-crypto",
"nym-mixnet-contract-common",
@@ -3109,6 +3116,7 @@ dependencies = [
"clap",
"clap_complete",
"clap_complete_fig",
"const-str",
"log",
"pretty_env_logger",
"schemars",
@@ -3317,6 +3325,7 @@ dependencies = [
"cfg-if",
"dotenvy",
"hex-literal",
"log",
"once_cell",
"schemars",
"serde",