-
Notifications
You must be signed in to change notification settings - Fork 234
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move cred verification to common crate
- Loading branch information
Showing
23 changed files
with
577 additions
and
461 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
[package] | ||
name = "nym-credential-verification" | ||
version = "0.1.0" | ||
authors.workspace = true | ||
repository.workspace = true | ||
homepage.workspace = true | ||
documentation.workspace = true | ||
edition.workspace = true | ||
license.workspace = true | ||
rust-version.workspace = true | ||
readme.workspace = true | ||
|
||
[dependencies] | ||
bs58 = { workspace = true } | ||
cosmwasm-std = { workspace = true } | ||
cw-utils = { workspace = true } | ||
futures = { workspace = true } | ||
rand = { workspace = true } | ||
si-scale = { workspace = true } | ||
thiserror = { workspace = true } | ||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } | ||
time = { workspace = true } | ||
tracing = { workspace = true } | ||
|
||
nym-api-requests = { path = "../../nym-api/nym-api-requests" } | ||
nym-credentials = { path = "../credentials" } | ||
nym-credentials-interface = { path = "../credentials-interface" } | ||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" } | ||
nym-ecash-double-spending = { path = "../ecash-double-spending" } | ||
nym-gateway-requests = { path = "../gateway-requests" } | ||
nym-gateway-storage = { path = "../gateway-storage" } | ||
nym-task = { path = "../task" } | ||
nym-validator-client = { path = "../client-libs/validator-client" } |
148 changes: 148 additions & 0 deletions
148
common/credential-verification/src/bandwidth_storage_manager.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// Copyright 2024 - Nym Technologies SA <[email protected]> | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use nym_credentials::ecash::utils::ecash_today; | ||
use nym_credentials_interface::Bandwidth; | ||
use nym_gateway_requests::ServerResponse; | ||
use nym_gateway_storage::Storage; | ||
use si_scale::helpers::bibytes2; | ||
use time::OffsetDateTime; | ||
use tracing::*; | ||
|
||
use crate::error::*; | ||
use crate::BandwidthFlushingBehaviourConfig; | ||
use crate::ClientBandwidth; | ||
|
||
const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB | ||
|
||
#[derive(Clone)] | ||
pub struct BandwidthStorageManager<S> { | ||
pub(crate) storage: S, | ||
pub(crate) client_bandwidth: ClientBandwidth, | ||
pub(crate) client_id: i64, | ||
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig, | ||
pub(crate) only_coconut_credentials: bool, | ||
} | ||
|
||
impl<S: Storage + Clone + 'static> BandwidthStorageManager<S> { | ||
pub fn new( | ||
storage: S, | ||
client_bandwidth: ClientBandwidth, | ||
client_id: i64, | ||
bandwidth_cfg: BandwidthFlushingBehaviourConfig, | ||
only_coconut_credentials: bool, | ||
) -> Self { | ||
BandwidthStorageManager { | ||
storage, | ||
client_bandwidth, | ||
client_id, | ||
bandwidth_cfg, | ||
only_coconut_credentials, | ||
} | ||
} | ||
|
||
async fn sync_expiration(&mut self) -> Result<()> { | ||
self.storage | ||
.set_expiration(self.client_id, self.client_bandwidth.bandwidth.expiration) | ||
.await?; | ||
Ok(()) | ||
} | ||
|
||
pub async fn handle_claim_testnet_bandwidth(&mut self) -> Result<ServerResponse> { | ||
debug!("handling testnet bandwidth request"); | ||
|
||
if self.only_coconut_credentials { | ||
return Err(Error::OnlyCoconutCredentials); | ||
} | ||
|
||
self.increase_bandwidth(FREE_TESTNET_BANDWIDTH_VALUE, ecash_today()) | ||
.await?; | ||
let available_total = self.client_bandwidth.bandwidth.bytes; | ||
|
||
Ok(ServerResponse::Bandwidth { available_total }) | ||
} | ||
|
||
#[instrument(skip_all)] | ||
pub async fn try_use_bandwidth(&mut self, required_bandwidth: i64) -> Result<i64> { | ||
if self.client_bandwidth.bandwidth.expired() { | ||
self.expire_bandwidth().await?; | ||
} | ||
let available_bandwidth = self.client_bandwidth.bandwidth.bytes; | ||
|
||
if available_bandwidth < required_bandwidth { | ||
return Err(Error::OutOfBandwidth { | ||
required: required_bandwidth, | ||
available: available_bandwidth, | ||
}); | ||
} | ||
|
||
let available_bi2 = bibytes2(available_bandwidth as f64); | ||
let required_bi2 = bibytes2(required_bandwidth as f64); | ||
debug!(available = available_bi2, required = required_bi2); | ||
|
||
self.consume_bandwidth(required_bandwidth).await?; | ||
Ok(available_bandwidth) | ||
} | ||
|
||
async fn expire_bandwidth(&mut self) -> Result<()> { | ||
self.storage.reset_bandwidth(self.client_id).await?; | ||
self.client_bandwidth.bandwidth = Default::default(); | ||
self.client_bandwidth.update_sync_data(); | ||
Ok(()) | ||
} | ||
|
||
/// Decreases the amount of available bandwidth of the connected client by the specified value. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `amount`: amount to decrease the available bandwidth by. | ||
async fn consume_bandwidth(&mut self, amount: i64) -> Result<()> { | ||
self.client_bandwidth.bandwidth.bytes -= amount; | ||
self.client_bandwidth.bytes_delta_since_sync -= amount; | ||
|
||
// since we're going to be operating on a fair use policy anyway, even if we crash and let extra few packets | ||
// through, that's completely fine | ||
if self.client_bandwidth.should_sync(self.bandwidth_cfg) { | ||
self.sync_bandwidth().await?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
#[instrument(level = "trace", skip_all)] | ||
async fn sync_bandwidth(&mut self) -> Result<()> { | ||
trace!("syncing client bandwidth with the underlying storage"); | ||
let updated = self | ||
.storage | ||
.increase_bandwidth(self.client_id, self.client_bandwidth.bytes_delta_since_sync) | ||
.await?; | ||
|
||
trace!(updated); | ||
|
||
self.client_bandwidth.bandwidth.bytes = updated; | ||
|
||
self.client_bandwidth.update_sync_data(); | ||
Ok(()) | ||
} | ||
|
||
/// Increases the amount of available bandwidth of the connected client by the specified value. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `amount`: amount to increase the available bandwidth by. | ||
/// * `expiration` : the expiration date of that bandwidth | ||
pub async fn increase_bandwidth( | ||
&mut self, | ||
bandwidth: Bandwidth, | ||
expiration: OffsetDateTime, | ||
) -> Result<()> { | ||
self.client_bandwidth.bandwidth.bytes += bandwidth.value() as i64; | ||
self.client_bandwidth.bytes_delta_since_sync += bandwidth.value() as i64; | ||
self.client_bandwidth.bandwidth.expiration = expiration; | ||
|
||
// any increases to bandwidth should get flushed immediately | ||
// (we don't want to accidentally miss somebody claiming a gigabyte voucher) | ||
self.sync_expiration().await?; | ||
self.sync_bandwidth().await | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright 2024 - Nym Technologies SA <[email protected]> | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use std::time::Duration; | ||
|
||
use nym_credentials_interface::AvailableBandwidth; | ||
use time::OffsetDateTime; | ||
|
||
#[derive(Debug, Clone, Copy)] | ||
pub struct BandwidthFlushingBehaviourConfig { | ||
/// Defines maximum delay between client bandwidth information being flushed to the persistent storage. | ||
pub client_bandwidth_max_flushing_rate: Duration, | ||
|
||
/// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage. | ||
pub client_bandwidth_max_delta_flushing_amount: i64, | ||
} | ||
|
||
#[derive(Debug, Clone, Copy)] | ||
pub struct ClientBandwidth { | ||
pub(crate) bandwidth: AvailableBandwidth, | ||
pub(crate) last_flushed: OffsetDateTime, | ||
|
||
/// the number of bytes the client had during the last sync. | ||
/// it is used to determine whether the current value should be synced with the storage | ||
/// by checking the delta with the known amount | ||
pub(crate) bytes_at_last_sync: i64, | ||
pub(crate) bytes_delta_since_sync: i64, | ||
} | ||
|
||
impl ClientBandwidth { | ||
pub fn new(bandwidth: AvailableBandwidth) -> ClientBandwidth { | ||
ClientBandwidth { | ||
bandwidth, | ||
last_flushed: OffsetDateTime::now_utc(), | ||
bytes_at_last_sync: bandwidth.bytes, | ||
bytes_delta_since_sync: 0, | ||
} | ||
} | ||
|
||
pub(crate) fn should_sync(&self, cfg: BandwidthFlushingBehaviourConfig) -> bool { | ||
if self.bytes_delta_since_sync.abs() >= cfg.client_bandwidth_max_delta_flushing_amount { | ||
return true; | ||
} | ||
|
||
if self.last_flushed + cfg.client_bandwidth_max_flushing_rate < OffsetDateTime::now_utc() { | ||
return true; | ||
} | ||
|
||
false | ||
} | ||
|
||
pub(crate) fn update_sync_data(&mut self) { | ||
self.last_flushed = OffsetDateTime::now_utc(); | ||
self.bytes_at_last_sync = self.bandwidth.bytes; | ||
self.bytes_delta_since_sync = 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,17 @@ | ||
// Copyright 2022-2024 - Nym Technologies SA <[email protected]> | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
|
||
use crate::node::client_handling::bandwidth::Bandwidth; | ||
use crate::node::client_handling::websocket::connection_handler::ecash::error::EcashTicketError; | ||
use crate::node::client_handling::websocket::connection_handler::ecash::helpers::for_each_api_concurrent; | ||
use crate::node::client_handling::websocket::connection_handler::ecash::state::SharedState; | ||
use crate::GatewayError; | ||
use crate::ecash::error::EcashTicketError; | ||
use crate::ecash::helpers::for_each_api_concurrent; | ||
use crate::ecash::state::SharedState; | ||
use crate::Error; | ||
use cosmwasm_std::Fraction; | ||
use cw_utils::ThresholdResponse; | ||
use futures::channel::mpsc::UnboundedReceiver; | ||
use futures::{Stream, StreamExt}; | ||
use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY; | ||
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody}; | ||
use nym_credentials_interface::Bandwidth; | ||
use nym_credentials_interface::{ClientTicket, TicketType}; | ||
use nym_gateway_storage::Storage; | ||
use nym_validator_client::nym_api::EpochId; | ||
|
@@ -105,25 +105,25 @@ impl PendingRedemptionVote { | |
} | ||
} | ||
|
||
pub(crate) struct CredentialHandlerConfig { | ||
pub struct CredentialHandlerConfig { | ||
/// Specifies the multiplier for revoking a malformed/double-spent ticket | ||
/// (if it has to go all the way to the nym-api for verification) | ||
/// e.g. if one ticket grants 100Mb and `revocation_bandwidth_penalty` is set to 1.5, | ||
/// the client will lose 150Mb | ||
pub(crate) revocation_bandwidth_penalty: f32, | ||
pub revocation_bandwidth_penalty: f32, | ||
|
||
/// Specifies the interval for attempting to resolve any failed, pending operations, | ||
/// such as ticket verification or redemption. | ||
pub(crate) pending_poller: Duration, | ||
pub pending_poller: Duration, | ||
|
||
pub(crate) minimum_api_quorum: f32, | ||
pub minimum_api_quorum: f32, | ||
|
||
/// Specifies the minimum number of tickets this gateway will attempt to redeem. | ||
pub(crate) minimum_redemption_tickets: usize, | ||
pub minimum_redemption_tickets: usize, | ||
|
||
/// Specifies the maximum time between two subsequent tickets redemptions. | ||
/// That's required as nym-apis will purge all ticket information for tickets older than 30 days. | ||
pub(crate) maximum_time_between_redemption: Duration, | ||
pub maximum_time_between_redemption: Duration, | ||
} | ||
|
||
pub(crate) struct CredentialHandler<St: Storage> { | ||
|
@@ -260,7 +260,7 @@ where | |
config: CredentialHandlerConfig, | ||
ticket_receiver: UnboundedReceiver<ClientTicket>, | ||
shared_state: SharedState<St>, | ||
) -> Result<Self, GatewayError> { | ||
) -> Result<Self, Error> { | ||
let multisig_threshold = shared_state | ||
.nyxd_client | ||
.read() | ||
|
@@ -269,7 +269,7 @@ where | |
.await?; | ||
|
||
let ThresholdResponse::AbsolutePercentage { percentage, .. } = multisig_threshold else { | ||
return Err(GatewayError::InvalidMultisigThreshold); | ||
return Err(Error::InvalidMultisigThreshold); | ||
}; | ||
|
||
// that's a nasty conversion, but it works : ) | ||
|
Oops, something went wrong.