From 0ee727bac1fe48e5208f95acd75f2ad61835aedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Wed, 7 Feb 2024 17:21:29 +0000 Subject: [PATCH] gateway handling of both credential types --- common/network-defaults/src/lib.rs | 3 + gateway/Cargo.toml | 1 + gateway/src/node/client_handling/bandwidth.rs | 91 +++++++++++++------ .../connection_handler/authenticated.rs | 11 +-- 4 files changed, 69 insertions(+), 37 deletions(-) diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index 4404b0e51f..1891cd3cc3 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -457,6 +457,9 @@ pub const ETH_ERC20_APPROVE_FUNCTION_NAME: &str = "approve"; /// How much bandwidth (in bytes) one token can buy pub const BYTES_PER_UTOKEN: u64 = 1024; +/// How much bandwidth (in bytes) one freepass provides +pub const BYTES_PER_FREEPASS: u64 = 1024 * 1024 * 1024; // 1GB + /// Threshold for claiming more bandwidth: 1 MB pub const REMAINING_BANDWIDTH_THRESHOLD: i64 = 1024 * 1024; /// How many ERC20 tokens should be burned to buy bandwidth diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index ff1fb32888..51f0307f76 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -55,6 +55,7 @@ tokio-stream = { version = "0.1.11", features = ["fs"] } tokio-tungstenite = { version = "0.20.1" } tokio-util = { workspace = true, features = ["codec"] } url = { workspace = true, features = ["serde"] } +time = { workspace = true } zeroize = { workspace = true } # internal diff --git a/gateway/src/node/client_handling/bandwidth.rs b/gateway/src/node/client_handling/bandwidth.rs index 76406360f6..ae8469c504 100644 --- a/gateway/src/node/client_handling/bandwidth.rs +++ b/gateway/src/node/client_handling/bandwidth.rs @@ -1,12 +1,40 @@ // Copyright 2021-2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use log::error; +use log::{error, warn}; use nym_credentials::coconut::bandwidth::CredentialType; +use std::num::ParseIntError; use thiserror::Error; +use time::error::ComponentRange; +use time::OffsetDateTime; #[derive(Debug, Error)] -pub enum BandwidthError {} +pub enum BandwidthError { + #[error("Provided bandwidth credential asks for more bandwidth than it is supported to add at once (credential value: {0}, supported: {}). Try to split it before attempting again", i64::MAX)] + UnsupportedBandwidthValue(u64), + + #[error("the provided free pass has already expired (expiry was on {expiry_date})")] + ExpiredFreePass { expiry_date: OffsetDateTime }, + + #[error("failed to pass the bandwidth voucher value: {source}")] + VoucherValueParsingFailure { + #[source] + source: ParseIntError, + }, + + #[error("failed to pass the free pass expiry date: {source}")] + ExpiryDateParsingFailure { + #[source] + source: ParseIntError, + }, + + #[error("failed to parse expiry timestamp into proper datetime: {source}")] + InvalidExpiryDate { + unix_timestamp: i64, + #[source] + source: ComponentRange, + }, +} pub struct Bandwidth { value: u64, @@ -18,42 +46,47 @@ impl Bandwidth { } pub fn try_from_raw_value(value: &String, typ: CredentialType) -> Result { - // let bandwidth_value = match credential.data.typ { - // CredentialType::Voucher => { - // todo!() - // } - // CredentialType::FreePass => { - // error!("unimplemented handling of free pass credential"); - // return Err(()); - // } - // }; + let bandwidth_value = + match typ { + CredentialType::Voucher => { + let token_value: u64 = value + .parse() + .map_err(|source| BandwidthError::VoucherValueParsingFailure { source })?; + token_value * nym_network_defaults::BYTES_PER_UTOKEN + } + CredentialType::FreePass => { + let expiry_timestamp: i64 = value + .parse() + .map_err(|source| BandwidthError::ExpiryDateParsingFailure { source })?; - /* - if bandwidth_value > i64::MAX as u64 { + let expiry_date = OffsetDateTime::from_unix_timestamp(expiry_timestamp) + .map_err(|source| BandwidthError::InvalidExpiryDate { + unix_timestamp: expiry_timestamp, + source, + })?; + let now = OffsetDateTime::now_utc(); + + if expiry_date < now { + return Err(BandwidthError::ExpiredFreePass { expiry_date }); + } + nym_network_defaults::BYTES_PER_FREEPASS + } + }; + + 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 + // 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, - )); + return Err(BandwidthError::UnsupportedBandwidthValue(bandwidth_value)); } - */ - todo!() + Ok(Bandwidth { + value: bandwidth_value, + }) } pub fn value(&self) -> u64 { self.value } } - -// impl From for Bandwidth { -// fn from(credential: Credential) -> Self { -// let token_value = credential.voucher_value(); -// let bandwidth_bytes = token_value * nym_network_defaults::BYTES_PER_UTOKEN; -// Bandwidth { -// value: bandwidth_bytes, -// } -// } -// } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index d5a662c2ed..3c3ff21fa4 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -53,9 +53,6 @@ pub(crate) enum RequestHandlingError { #[error("The received request is not valid in the current context")] IllegalRequest, - #[error("Provided bandwidth credential asks for more bandwidth than it is supported to add at once (credential value: {0}, supported: {}). Try to split it before attempting again", i64::MAX)] - UnsupportedBandwidthValue(u64), - #[error("Provided bandwidth credential did not verify correctly on {0}")] InvalidBandwidthCredential(String), @@ -85,9 +82,6 @@ pub(crate) enum RequestHandlingError { #[error("failed to recover bandwidth value: {0}")] BandwidthRecoveryFailure(#[from] BandwidthError), - - #[error("free pass credentials haven't been implemented yet")] - UnimplementedFreePass, } impl RequestHandlingError { @@ -250,6 +244,7 @@ where unimplemented!() }; + // this will extract token amounts out of bandwidth vouchers and validate expiry of free passes let bandwidth = Bandwidth::try_from_raw_value(bandwidth_attribute, credential.data.typ)?; let params = bandwidth_credential_params(); @@ -273,8 +268,8 @@ where .await?; } CredentialType::FreePass => { - error!("unimplemented handling of free pass credential"); - return Err(RequestHandlingError::UnimplementedFreePass); + // no need to do anything special here, we already extracted the bandwidth amount and checked expiry + info!("received a free pass credential"); } }