Skip to content

Commit

Permalink
Feature/consumable bandwidth (#766)
Browse files Browse the repository at this point in the history
* Set actual value for bandwidth

Also put it as a public attribute, such that it can be actively used
by the credential consumer

* Switch from sending Attribute structs to sending the actual attribute bytes over the wire

* Add atomic bandwidth value to gateway

* Consume bandwidth based on the mix packet size

* Use Bandwidth struct for specific functionality

* Move bandwidth code outside the dependency path of wasm client

* Use u64 instead of AtomicU64, as the handling is not parallel
  • Loading branch information
neacsu authored Sep 9, 2021
1 parent e00e77d commit c9dce0c
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 50 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 16 additions & 6 deletions clients/tauri-client/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
)]

use coconut_interface::{
self, Attribute, Credential, Parameters, Signature, Theta, VerificationKey,
self, hash_to_scalar, Attribute, Credential, Parameters, Signature, Theta, VerificationKey,
};
use credentials::{obtain_aggregate_signature, obtain_aggregate_verification_key};
use std::sync::Arc;
Expand All @@ -15,19 +15,29 @@ struct State {
signatures: Vec<Signature>,
n_attributes: u32,
params: Parameters,
public_attributes_bytes: Vec<Vec<u8>>,
public_attributes: Vec<Attribute>,
private_attributes: Vec<Attribute>,
aggregated_verification_key: Option<VerificationKey>,
}

impl State {
fn init(public_attributes: Vec<Attribute>, private_attributes: Vec<Attribute>) -> State {
let n_attributes = (public_attributes.len() + private_attributes.len()) as u32;
fn init(public_attributes_bytes: Vec<Vec<u8>>, private_attributes_bytes: Vec<Vec<u8>>) -> State {
let n_attributes = (public_attributes_bytes.len() + private_attributes_bytes.len()) as u32;
let params = Parameters::new(n_attributes).unwrap();
let public_attributes = public_attributes_bytes
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
let private_attributes = private_attributes_bytes
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
State {
signatures: Vec::new(),
n_attributes,
params,
public_attributes_bytes,
public_attributes,
private_attributes,
aggregated_verification_key: None,
Expand Down Expand Up @@ -137,7 +147,7 @@ async fn verify_credential(
let credential = Credential::new(
state.n_attributes,
theta,
&state.public_attributes,
state.public_attributes_bytes.clone(),
state
.signatures
.get(idx)
Expand Down Expand Up @@ -170,8 +180,8 @@ async fn get_credential(
}

fn main() {
let public_attributes = vec![coconut_interface::hash_to_scalar("public_key")];
let private_attributes = vec![coconut_interface::hash_to_scalar("private_key")];
let public_attributes = vec![b"public_key".to_vec()];
let private_attributes = vec![b"private_key".to_vec()];
tauri::Builder::default()
.manage(Arc::new(RwLock::new(State::init(
public_attributes,
Expand Down
28 changes: 11 additions & 17 deletions common/coconut-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,37 @@ pub struct Credential {
n_params: u32,
#[getset(get = "pub")]
theta: Theta,
public_attributes: Vec<String>,
public_attributes: Vec<Vec<u8>>,
#[getset(get = "pub")]
signature: Signature,
}
impl Credential {
pub fn new(
n_params: u32,
theta: Theta,
public_attributes: &[Attribute],
public_attributes: Vec<Vec<u8>>,
signature: &Signature,
) -> Credential {
Credential {
n_params,
theta,
public_attributes: public_attributes
.iter()
.map(|attr| attr.to_bs58())
.collect(),
public_attributes,
signature: *signature,
}
}

pub fn public_attributes(&self) -> Vec<Attribute> {
self.public_attributes
.iter()
.map(|x| Attribute::try_from_bs58(x).unwrap())
.collect()
pub fn public_attributes(&self) -> Vec<Vec<u8>> {
self.public_attributes.clone()
}

pub fn verify(&self, verification_key: &VerificationKey) -> bool {
let params = Parameters::new(self.n_params).unwrap();
coconut_rs::verify_credential(
&params,
verification_key,
&self.theta,
&self.public_attributes(),
)
let public_attributes = self
.public_attributes
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
coconut_rs::verify_credential(&params, verification_key, &self.theta, &public_attributes)
}
}

Expand Down
17 changes: 9 additions & 8 deletions common/credentials/src/bandwidth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@
// right now this has no double-spending protection, spender binding, etc
// it's the simplest possible case

use url::Url;

use crate::error::Error;
use crate::utils::{obtain_aggregate_signature, prepare_credential_for_spending};
use coconut_interface::{hash_to_scalar, Credential, Parameters, Signature, VerificationKey};
use url::Url;

const BANDWIDTH_VALUE: &str = "Bandwidth: infinite (for now)";
const BANDWIDTH_VALUE: u64 = 1024 * 1024; // 1 MB

pub const PUBLIC_ATTRIBUTES: u32 = 1;
pub const PRIVATE_ATTRIBUTES: u32 = 1;
pub const TOTAL_ATTRIBUTES: u32 = PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES;

// TODO: this definitely has to be moved somewhere else. It's just a temporary solution
pub async fn obtain_signature(raw_identity: &[u8], validators: &[Url]) -> Result<Signature, Error> {
let public_attributes = vec![hash_to_scalar(raw_identity)];
let private_attributes = vec![hash_to_scalar(BANDWIDTH_VALUE)];
let public_attributes = vec![hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes())];
let private_attributes = vec![hash_to_scalar(raw_identity)];

let params = Parameters::new(TOTAL_ATTRIBUTES)?;

Expand All @@ -32,15 +33,15 @@ pub fn prepare_for_spending(
signature: &Signature,
verification_key: &VerificationKey,
) -> Result<Credential, Error> {
let public_attributes = vec![hash_to_scalar(raw_identity)];
let private_attributes = vec![hash_to_scalar(BANDWIDTH_VALUE)];
let public_attributes = vec![BANDWIDTH_VALUE.to_be_bytes().to_vec()];
let private_attributes = vec![raw_identity.to_vec()];

let params = Parameters::new(TOTAL_ATTRIBUTES)?;

prepare_credential_for_spending(
&params,
&public_attributes,
&private_attributes,
public_attributes,
private_attributes,
signature,
verification_key,
)
Expand Down
12 changes: 12 additions & 0 deletions common/credentials/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,16 @@ pub enum Error {

#[error("Run into a validato client error - {0}")]
ValidatorClientError(#[from] ValidatorClientError),

#[error("Not enough public attributes were specified")]
NotEnoughPublicAttributes,

#[error("Bandwidth is expected to be represented on 8 bytes")]
InvalidBandwidthSize,

#[error("Bandwidth operation overflowed. {0}")]
BandwidthOverflow(String),

#[error("There is not associated bandwidth for the given client")]
MissingBandwidth,
}
16 changes: 10 additions & 6 deletions common/credentials/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

use crate::error::Error;
use coconut_interface::{
aggregate_signature_shares, aggregate_verification_keys, prepare_blind_sign, prove_credential,
Attribute, BlindSignRequestBody, Credential, Parameters, Signature, SignatureShare,
VerificationKey,
aggregate_signature_shares, aggregate_verification_keys, hash_to_scalar, prepare_blind_sign,
prove_credential, Attribute, BlindSignRequestBody, Credential, Parameters, Signature,
SignatureShare, VerificationKey,
};
use url::Url;

Expand Down Expand Up @@ -118,12 +118,16 @@ pub async fn obtain_aggregate_signature(
// TODO: better type flow
pub fn prepare_credential_for_spending(
params: &Parameters,
public_attributes: &[Attribute],
private_attributes: &[Attribute],
public_attributes: Vec<Vec<u8>>,
private_attributes: Vec<Vec<u8>>,
signature: &Signature,
verification_key: &VerificationKey,
) -> Result<Credential, Error> {
let theta = prove_credential(params, verification_key, signature, private_attributes)?;
let private_attributes = private_attributes
.iter()
.map(hash_to_scalar)
.collect::<Vec<Attribute>>();
let theta = prove_credential(params, verification_key, signature, &private_attributes)?;

Ok(Credential::new(
(public_attributes.len() + private_attributes.len()) as u32,
Expand Down
1 change: 1 addition & 0 deletions gateway/gateway-requests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ crypto = { path = "../../common/crypto" }
pemstore = { path = "../../common/pemstore" }

coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials"}

[dependencies.tungstenite]
version = "0.13.0"
Expand Down
85 changes: 85 additions & 0 deletions gateway/src/node/client_handling/bandwidth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0

use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::Arc;
use tokio::sync::RwLock;

use coconut_interface::Credential;
use credentials::error::Error;
use nymsphinx::DestinationAddressBytes;

const BANDWIDTH_INDEX: usize = 0;

pub type BandwidthDatabase = Arc<RwLock<HashMap<DestinationAddressBytes, u64>>>;

pub fn empty_bandwidth_database() -> BandwidthDatabase {
Arc::new(RwLock::new(HashMap::new()))
}

pub struct Bandwidth {
value: u64,
}

impl Bandwidth {
pub fn value(&self) -> u64 {
self.value
}

pub async fn consume_bandwidth(
bandwidths: &BandwidthDatabase,
remote_address: &DestinationAddressBytes,
consumed: u64,
) -> Result<(), Error> {
if let Some(bandwidth) = bandwidths.write().await.get_mut(remote_address) {
if let Some(res) = bandwidth.checked_sub(consumed) {
*bandwidth = res;
Ok(())
} else {
Err(Error::BandwidthOverflow(String::from(
"Allocate more bandwidth for consumption",
)))
}
} else {
Err(Error::MissingBandwidth)
}
}

pub async fn increase_bandwidth(
bandwidths: &BandwidthDatabase,
remote_address: &DestinationAddressBytes,
increase: u64,
) -> Result<(), Error> {
let mut db = bandwidths.write().await;
if let Some(bandwidth) = db.get_mut(remote_address) {
if let Some(new_bandwidth) = bandwidth.checked_add(increase) {
*bandwidth = new_bandwidth;
} else {
return Err(Error::BandwidthOverflow(String::from(
"Use some of the already allocated bandwidth",
)));
}
} else {
db.insert(*remote_address, increase);
}
Ok(())
}
}

impl TryFrom<Credential> for Bandwidth {
type Error = Error;

fn try_from(credential: Credential) -> Result<Self, Self::Error> {
match credential.public_attributes().get(BANDWIDTH_INDEX) {
None => Err(Error::NotEnoughPublicAttributes),
Some(attr) => match <[u8; 8]>::try_from(attr.as_slice()) {
Ok(bandwidth_bytes) => {
let value = u64::from_be_bytes(bandwidth_bytes);
Ok(Self { value })
}
Err(_) => Err(Error::InvalidBandwidthSize),
},
}
}
}
1 change: 1 addition & 0 deletions gateway/src/node/client_handling/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0

mod bandwidth;
pub(crate) mod clients_handler;
pub(crate) mod websocket;
Loading

0 comments on commit c9dce0c

Please sign in to comment.