updated all ecash-related parameters - bloomfilter, expiration, sizes, etc.
This commit is contained in:
Generated
+1
-4
@@ -4726,6 +4726,7 @@ dependencies = [
|
||||
"ff",
|
||||
"group",
|
||||
"itertools 0.12.1",
|
||||
"nym-network-defaults",
|
||||
"nym-pemstore",
|
||||
"rand 0.8.5",
|
||||
"rayon",
|
||||
@@ -5367,14 +5368,10 @@ dependencies = [
|
||||
name = "nym-network-defaults"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dotenvy",
|
||||
"hex-literal",
|
||||
"log",
|
||||
"once_cell",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::error::ClientCoreError;
|
||||
use nym_credential_storage::models::BasicTicketbookInformation;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_ecash_time::ecash_today;
|
||||
use nym_network_defaults::TICKET_BANDWIDTH_VALUE;
|
||||
use nym_network_defaults::TicketbookType::MixnetEntry;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::Date;
|
||||
|
||||
@@ -62,7 +62,9 @@ impl From<BasicTicketbookInformation> for AvailableTicketbook {
|
||||
expiration: value.expiration_date,
|
||||
issued_tickets: value.total_tickets,
|
||||
claimed_tickets: value.used_tickets,
|
||||
ticket_size: TICKET_BANDWIDTH_VALUE,
|
||||
|
||||
// TODO: this will change when 'type' field is introduced; for now doesn't matter what we put there
|
||||
ticket_size: MixnetEntry.bandwidth_value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_gateway_client::client::config::GatewayClientConfig;
|
||||
use nym_gateway_client::{
|
||||
AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter,
|
||||
};
|
||||
@@ -403,6 +404,11 @@ where
|
||||
gateway_listener,
|
||||
);
|
||||
GatewayClient::new(
|
||||
GatewayClientConfig::new_default()
|
||||
.with_disabled_credentials_mode(config.client.disabled_credentials_mode)
|
||||
.with_response_timeout(
|
||||
config.debug.gateway_connection.gateway_response_timeout,
|
||||
),
|
||||
cfg,
|
||||
managed_keys.identity_keypair(),
|
||||
Some(details.derived_aes128_ctr_blake3_hmac_keys),
|
||||
@@ -410,8 +416,6 @@ where
|
||||
bandwidth_controller,
|
||||
shutdown,
|
||||
)
|
||||
.with_disabled_credentials_mode(config.client.disabled_credentials_mode)
|
||||
.with_response_timeout(config.debug.gateway_connection.gateway_response_timeout)
|
||||
};
|
||||
|
||||
gateway_client
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use nym_network_defaults::TicketbookType::MixnetEntry;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct GatewayClientConfig {
|
||||
pub connection: Connection,
|
||||
pub bandwidth: BandwidthTickets,
|
||||
}
|
||||
|
||||
impl GatewayClientConfig {
|
||||
pub fn new_default() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
|
||||
self.bandwidth.require_tickets = !disabled_credentials_mode;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_reconnection_on_failure(mut self, should_reconnect_on_failure: bool) -> Self {
|
||||
self.connection.should_reconnect_on_failure = should_reconnect_on_failure;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_response_timeout(mut self, response_timeout_duration: Duration) -> Self {
|
||||
self.connection.response_timeout_duration = response_timeout_duration;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_reconnection_attempts(mut self, reconnection_attempts: usize) -> Self {
|
||||
self.connection.reconnection_attempts = reconnection_attempts;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_reconnection_backoff(mut self, backoff: Duration) -> Self {
|
||||
self.connection.reconnection_backoff = backoff;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Connection {
|
||||
/// Specifies the timeout for gateway responses
|
||||
pub response_timeout_duration: Duration,
|
||||
|
||||
/// Specifies whether client should try to reconnect to gateway on connection failure.
|
||||
pub should_reconnect_on_failure: bool,
|
||||
|
||||
/// Specifies maximum number of attempts client will try to reconnect to gateway on failure
|
||||
/// before giving up.
|
||||
pub reconnection_attempts: usize,
|
||||
|
||||
/// Delay between each subsequent reconnection attempt.
|
||||
pub reconnection_backoff: Duration,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
|
||||
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
|
||||
// bandwidth bridging protocol, we can come back to a smaller timeout value
|
||||
pub const DEFAULT_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
||||
pub const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10;
|
||||
pub const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
|
||||
}
|
||||
|
||||
impl Default for Connection {
|
||||
fn default() -> Self {
|
||||
Connection {
|
||||
response_timeout_duration: Self::DEFAULT_RESPONSE_TIMEOUT,
|
||||
should_reconnect_on_failure: true,
|
||||
reconnection_attempts: Self::DEFAULT_RECONNECTION_ATTEMPTS,
|
||||
reconnection_backoff: Self::DEFAULT_RECONNECTION_BACKOFF,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BandwidthTickets {
|
||||
/// specifies whether this client will be sending bandwidth tickets or will attempt to use 'free' testnet bandwidth instead
|
||||
pub require_tickets: bool,
|
||||
|
||||
/// specifies threshold (in bytes) under which the client should send another ticket to the gateway
|
||||
pub remaining_bandwidth_threshold: i64,
|
||||
|
||||
/// specifies threshold (in bytes) under which the client will NOT send any tickets because it got accused of double spending and got its bandwidth revoked
|
||||
/// if not specified, the client will always send tickets
|
||||
pub cutoff_remaining_bandwidth_threshold: Option<i64>,
|
||||
}
|
||||
|
||||
impl BandwidthTickets {
|
||||
// TO BE CHANGED \/
|
||||
pub const DEFAULT_REQUIRES_TICKETS: bool = false;
|
||||
|
||||
// 20% of entry ticket value
|
||||
pub const DEFAULT_REMAINING_BANDWIDTH_THRESHOLD: i64 =
|
||||
(MixnetEntry.bandwidth_value() / 5) as i64;
|
||||
|
||||
pub const DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD: Option<i64> = None;
|
||||
|
||||
pub fn ensure_above_cutoff(&self, available: i64) -> Result<(), GatewayClientError> {
|
||||
if let Some(cutoff) = self.cutoff_remaining_bandwidth_threshold {
|
||||
if available < cutoff {
|
||||
let available_bi2 = bibytes2(available as f64);
|
||||
let cutoff_bi2 = bibytes2(cutoff as f64);
|
||||
return Err(GatewayClientError::BandwidthBelowCutoffValue {
|
||||
available_bi2,
|
||||
cutoff_bi2,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BandwidthTickets {
|
||||
fn default() -> Self {
|
||||
BandwidthTickets {
|
||||
require_tickets: Self::DEFAULT_REQUIRES_TICKETS,
|
||||
remaining_bandwidth_threshold: Self::DEFAULT_REMAINING_BANDWIDTH_THRESHOLD,
|
||||
cutoff_remaining_bandwidth_threshold:
|
||||
Self::DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD,
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-83
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::bandwidth::ClientBandwidth;
|
||||
use crate::client::config::GatewayClientConfig;
|
||||
use crate::error::GatewayClientError;
|
||||
use crate::packet_router::PacketRouter;
|
||||
pub use crate::packet_router::{
|
||||
@@ -23,13 +24,11 @@ use nym_gateway_requests::{
|
||||
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
|
||||
CURRENT_PROTOCOL_VERSION,
|
||||
};
|
||||
use nym_network_defaults::REMAINING_BANDWIDTH_THRESHOLD;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_task::TaskClient;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use rand::rngs::OsRng;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tungstenite::protocol::Message;
|
||||
use url::Url;
|
||||
|
||||
@@ -40,7 +39,6 @@ use tokio::time::sleep;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
|
||||
use crate::bandwidth::ClientBandwidth;
|
||||
#[cfg(not(unix))]
|
||||
use std::os::raw::c_int as RawFd;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -48,12 +46,7 @@ use wasm_utils::websocket::JSWebsocket;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasmtimer::tokio::sleep;
|
||||
|
||||
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
|
||||
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
|
||||
// bandwidth bridging protocol, we can come back to a smaller timeout value
|
||||
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
||||
const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10;
|
||||
const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
|
||||
pub mod config;
|
||||
|
||||
pub struct GatewayConfig {
|
||||
pub gateway_identity: identity::PublicKey,
|
||||
@@ -80,8 +73,9 @@ impl GatewayConfig {
|
||||
|
||||
// TODO: this should be refactored into a state machine that keeps track of its authentication state
|
||||
pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
pub cfg: GatewayClientConfig,
|
||||
|
||||
authenticated: bool,
|
||||
disabled_credentials_mode: bool,
|
||||
bandwidth: ClientBandwidth,
|
||||
gateway_address: String,
|
||||
gateway_identity: identity::PublicKey,
|
||||
@@ -89,18 +83,8 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
shared_key: Option<Arc<SharedKeys>>,
|
||||
connection: SocketState,
|
||||
packet_router: PacketRouter,
|
||||
response_timeout_duration: Duration,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
|
||||
// reconnection related variables
|
||||
/// Specifies whether client should try to reconnect to gateway on connection failure.
|
||||
should_reconnect_on_failure: bool,
|
||||
/// Specifies maximum number of attempts client will try to reconnect to gateway on failure
|
||||
/// before giving up.
|
||||
reconnection_attempts: usize,
|
||||
/// Delay between each subsequent reconnection attempt.
|
||||
reconnection_backoff: Duration,
|
||||
|
||||
// currently unused (but populated)
|
||||
negotiated_protocol: Option<u8>,
|
||||
|
||||
@@ -110,7 +94,8 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
|
||||
impl<C, St> GatewayClient<C, St> {
|
||||
pub fn new(
|
||||
config: GatewayConfig,
|
||||
cfg: GatewayClientConfig,
|
||||
gateway_config: GatewayConfig,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
// TODO: make it mandatory. if you don't want to pass it, use `new_init`
|
||||
shared_key: Option<Arc<SharedKeys>>,
|
||||
@@ -119,55 +104,21 @@ impl<C, St> GatewayClient<C, St> {
|
||||
task_client: TaskClient,
|
||||
) -> Self {
|
||||
GatewayClient {
|
||||
cfg,
|
||||
authenticated: false,
|
||||
disabled_credentials_mode: true,
|
||||
bandwidth: ClientBandwidth::new_empty(),
|
||||
gateway_address: config.gateway_listener,
|
||||
gateway_identity: config.gateway_identity,
|
||||
gateway_address: gateway_config.gateway_listener,
|
||||
gateway_identity: gateway_config.gateway_identity,
|
||||
local_identity,
|
||||
shared_key,
|
||||
connection: SocketState::NotConnected,
|
||||
packet_router,
|
||||
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||
bandwidth_controller,
|
||||
should_reconnect_on_failure: true,
|
||||
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
|
||||
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
|
||||
negotiated_protocol: None,
|
||||
task_client,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
|
||||
self.disabled_credentials_mode = disabled_credentials_mode;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_reconnection_on_failure(mut self, should_reconnect_on_failure: bool) -> Self {
|
||||
self.should_reconnect_on_failure = should_reconnect_on_failure;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_response_timeout(mut self, response_timeout_duration: Duration) -> Self {
|
||||
self.response_timeout_duration = response_timeout_duration;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_reconnection_attempts(mut self, reconnection_attempts: usize) -> Self {
|
||||
self.reconnection_attempts = reconnection_attempts;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_reconnection_backoff(mut self, backoff: Duration) -> Self {
|
||||
self.reconnection_backoff = backoff;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn gateway_identity(&self) -> identity::PublicKey {
|
||||
self.gateway_identity
|
||||
}
|
||||
@@ -257,18 +208,21 @@ impl<C, St> GatewayClient<C, St> {
|
||||
info!("Attempting gateway reconnection...");
|
||||
self.authenticated = false;
|
||||
|
||||
for i in 1..self.reconnection_attempts {
|
||||
for i in 1..self.cfg.connection.reconnection_attempts {
|
||||
info!("reconnection attempt {}...", i);
|
||||
if self.try_reconnect().await.is_ok() {
|
||||
info!("managed to reconnect!");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
sleep(self.reconnection_backoff).await;
|
||||
sleep(self.cfg.connection.reconnection_backoff).await;
|
||||
}
|
||||
|
||||
// final attempt (done separately to be able to return a proper error)
|
||||
info!("reconnection attempt {}", self.reconnection_attempts);
|
||||
info!(
|
||||
"reconnection attempt {}",
|
||||
self.cfg.connection.reconnection_attempts
|
||||
);
|
||||
match self.try_reconnect().await {
|
||||
Ok(_) => {
|
||||
info!("managed to reconnect!");
|
||||
@@ -277,7 +231,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Err(err) => {
|
||||
error!(
|
||||
"failed to reconnect after {} attempts",
|
||||
self.reconnection_attempts
|
||||
self.cfg.connection.reconnection_attempts
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
@@ -293,7 +247,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
_ => return Err(GatewayClientError::ConnectionInInvalidState),
|
||||
};
|
||||
|
||||
let timeout = sleep(self.response_timeout_duration);
|
||||
let timeout = sleep(self.cfg.connection.response_timeout_duration);
|
||||
tokio::pin!(timeout);
|
||||
|
||||
loop {
|
||||
@@ -462,7 +416,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
ws_stream,
|
||||
self.local_identity.as_ref(),
|
||||
self.gateway_identity,
|
||||
!self.disabled_credentials_mode,
|
||||
self.cfg.bandwidth.require_tickets,
|
||||
)
|
||||
.await
|
||||
.map_err(GatewayClientError::RegistrationFailure),
|
||||
@@ -524,7 +478,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self_address,
|
||||
encrypted_address,
|
||||
iv,
|
||||
!self.disabled_credentials_mode,
|
||||
self.cfg.bandwidth.require_tickets,
|
||||
)
|
||||
.into();
|
||||
|
||||
@@ -638,12 +592,12 @@ impl<C, St> GatewayClient<C, St> {
|
||||
if self.shared_key.is_none() {
|
||||
return Err(GatewayClientError::NoSharedKeyAvailable);
|
||||
}
|
||||
if self.bandwidth_controller.is_none() && !self.disabled_credentials_mode {
|
||||
if self.bandwidth_controller.is_none() && self.cfg.bandwidth.require_tickets {
|
||||
return Err(GatewayClientError::NoBandwidthControllerAvailable);
|
||||
}
|
||||
|
||||
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
|
||||
if self.disabled_credentials_mode {
|
||||
if !self.cfg.bandwidth.require_tickets {
|
||||
info!("The client is running in disabled credentials mode - attempting to claim bandwidth without a credential");
|
||||
return self.try_claim_testnet_bandwidth().await;
|
||||
}
|
||||
@@ -698,7 +652,10 @@ impl<C, St> GatewayClient<C, St> {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
}
|
||||
let bandwidth_remaining = self.bandwidth.remaining();
|
||||
if bandwidth_remaining < REMAINING_BANDWIDTH_THRESHOLD {
|
||||
if bandwidth_remaining < self.cfg.bandwidth.remaining_bandwidth_threshold {
|
||||
self.cfg
|
||||
.bandwidth
|
||||
.ensure_above_cutoff(bandwidth_remaining)?;
|
||||
self.claim_bandwidth().await?;
|
||||
}
|
||||
|
||||
@@ -721,7 +678,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
.batch_send_websocket_messages_without_response(messages)
|
||||
.await
|
||||
{
|
||||
if err.is_closed_connection() && self.should_reconnect_on_failure {
|
||||
if err.is_closed_connection() && self.cfg.connection.should_reconnect_on_failure {
|
||||
self.attempt_reconnection().await
|
||||
} else {
|
||||
Err(err)
|
||||
@@ -736,7 +693,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
msg: Message,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if let Err(err) = self.send_websocket_message_without_response(msg).await {
|
||||
if err.is_closed_connection() && self.should_reconnect_on_failure {
|
||||
if err.is_closed_connection() && self.cfg.connection.should_reconnect_on_failure {
|
||||
debug!("Going to attempt a reconnection");
|
||||
self.attempt_reconnection().await
|
||||
} else {
|
||||
@@ -770,7 +727,10 @@ impl<C, St> GatewayClient<C, St> {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
}
|
||||
let bandwidth_remaining = self.bandwidth.remaining();
|
||||
if bandwidth_remaining < REMAINING_BANDWIDTH_THRESHOLD {
|
||||
if bandwidth_remaining < self.cfg.bandwidth.remaining_bandwidth_threshold {
|
||||
self.cfg
|
||||
.bandwidth
|
||||
.ensure_above_cutoff(bandwidth_remaining)?;
|
||||
self.claim_bandwidth().await?;
|
||||
}
|
||||
|
||||
@@ -869,7 +829,10 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
let shared_key = self.perform_initial_authentication().await?;
|
||||
let bandwidth_remaining = self.bandwidth.remaining();
|
||||
if bandwidth_remaining < REMAINING_BANDWIDTH_THRESHOLD {
|
||||
if bandwidth_remaining < self.cfg.bandwidth.remaining_bandwidth_threshold {
|
||||
self.cfg
|
||||
.bandwidth
|
||||
.ensure_above_cutoff(bandwidth_remaining)?;
|
||||
info!("Claiming more bandwidth with existing credentials. Stop the process now if you don't want that to happen.");
|
||||
self.claim_bandwidth().await?;
|
||||
}
|
||||
@@ -905,8 +868,8 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
let packet_router = PacketRouter::new(ack_tx, mix_tx, task_client.clone());
|
||||
|
||||
GatewayClient {
|
||||
cfg: GatewayClientConfig::default().with_disabled_credentials_mode(true),
|
||||
authenticated: false,
|
||||
disabled_credentials_mode: true,
|
||||
bandwidth: ClientBandwidth::new_empty(),
|
||||
gateway_address: gateway_listener.to_string(),
|
||||
gateway_identity,
|
||||
@@ -914,11 +877,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
shared_key: None,
|
||||
connection: SocketState::NotConnected,
|
||||
packet_router,
|
||||
response_timeout_duration: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||
bandwidth_controller: None,
|
||||
should_reconnect_on_failure: false,
|
||||
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
|
||||
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
|
||||
negotiated_protocol: None,
|
||||
task_client,
|
||||
}
|
||||
@@ -937,8 +896,8 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
assert!(self.shared_key.is_some());
|
||||
|
||||
GatewayClient {
|
||||
cfg: self.cfg,
|
||||
authenticated: self.authenticated,
|
||||
disabled_credentials_mode: self.disabled_credentials_mode,
|
||||
bandwidth: self.bandwidth,
|
||||
gateway_address: self.gateway_address,
|
||||
gateway_identity: self.gateway_identity,
|
||||
@@ -946,11 +905,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
shared_key: self.shared_key,
|
||||
connection: self.connection,
|
||||
packet_router,
|
||||
response_timeout_duration: self.response_timeout_duration,
|
||||
bandwidth_controller,
|
||||
should_reconnect_on_failure: self.should_reconnect_on_failure,
|
||||
reconnection_attempts: self.reconnection_attempts,
|
||||
reconnection_backoff: self.reconnection_backoff,
|
||||
negotiated_protocol: self.negotiated_protocol,
|
||||
task_client,
|
||||
}
|
||||
@@ -67,6 +67,12 @@ pub enum GatewayClientError {
|
||||
#[error("There are no more bandwidth credentials acquired. Please buy some more if you want to use the mixnet")]
|
||||
NoMoreBandwidthCredentials,
|
||||
|
||||
#[error("the current available bandwidth ({available_bi2}) is below the minimum cutoff threshold off {cutoff_bi2}")]
|
||||
BandwidthBelowCutoffValue {
|
||||
available_bi2: String,
|
||||
cutoff_bi2: String,
|
||||
},
|
||||
|
||||
#[error("Received an unexpected response")]
|
||||
UnexpectedResponse,
|
||||
|
||||
|
||||
@@ -8,12 +8,16 @@ license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
cfg-if = { workspace = true }
|
||||
dotenvy = { workspace = true }
|
||||
hex-literal = { workspace = true }
|
||||
log = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
dotenvy = { workspace = true, optional = true }
|
||||
log = { workspace = true, optional = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||
serde = { workspace = true, features = ["derive"], optional = true }
|
||||
url = { workspace = true, optional = true }
|
||||
|
||||
# please be extremely careful when adding new dependencies because this crate is imported by the ecash contract,
|
||||
# so if anything new is added, consider feature-locking it and then just adding it to default feature
|
||||
|
||||
[features]
|
||||
default = ["env", "network"]
|
||||
env = ["dotenvy", "log"]
|
||||
network = ["schemars", "serde", "url"]
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// re-export to not break existing imports
|
||||
pub use deprecated::*;
|
||||
pub use nyx::*;
|
||||
pub use wireguard::*;
|
||||
|
||||
// all of those should be obtained via nym-node, et al. crate instead
|
||||
pub mod deprecated {
|
||||
pub const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
|
||||
|
||||
// 'GATEWAY'
|
||||
pub const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000;
|
||||
|
||||
// 'MIXNODE'
|
||||
pub const DEFAULT_VERLOC_LISTENING_PORT: u16 = 1790;
|
||||
pub const DEFAULT_HTTP_API_LISTENING_PORT: u16 = 8000;
|
||||
|
||||
// 'CLIENT'
|
||||
pub const DEFAULT_WEBSOCKET_LISTENING_PORT: u16 = 1977;
|
||||
|
||||
// 'SOCKS5' CLIENT
|
||||
pub const DEFAULT_SOCKS5_LISTENING_PORT: u16 = 1080;
|
||||
|
||||
// NYM-API
|
||||
pub const DEFAULT_NYM_API_PORT: u16 = 8080;
|
||||
|
||||
pub const NYM_API_VERSION: &str = "v1";
|
||||
|
||||
// NYM-NODE
|
||||
pub const DEFAULT_NYM_NODE_HTTP_PORT: u16 = 8080;
|
||||
}
|
||||
|
||||
pub mod nyx {
|
||||
/// Defaults Cosmos Hub/ATOM path
|
||||
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
||||
|
||||
// as set by validators in their configs
|
||||
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
||||
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
||||
|
||||
// TODO: is there a way to get this from the chain
|
||||
pub const TOTAL_SUPPLY: u128 = 1_000_000_000_000_000;
|
||||
}
|
||||
|
||||
pub mod wireguard {
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
pub const WG_PORT: u16 = 51822;
|
||||
|
||||
// The interface used to route traffic
|
||||
pub const WG_TUN_BASE_NAME: &str = "nymwg";
|
||||
pub const WG_TUN_DEVICE_ADDRESS: &str = "10.1.0.1";
|
||||
pub const WG_TUN_DEVICE_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(10, 1, 0, 1));
|
||||
pub const WG_TUN_DEVICE_NETMASK: &str = "255.255.255.0";
|
||||
}
|
||||
@@ -1,20 +1,47 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// How much bandwidth (in bytes) one ticket can buy
|
||||
pub const TICKET_BANDWIDTH_VALUE: u64 = 100 * 1024 * 1024; // 100 MB
|
||||
/// Specifies the maximum validity of the issued ticketbooks.
|
||||
pub const TICKETBOOK_VALIDITY_DAYS: u64 = 7;
|
||||
|
||||
///Tickets to spend per payment
|
||||
pub const SPEND_TICKETS: u64 = 1;
|
||||
/// Threshold for claiming more bandwidth: 1 MB
|
||||
pub const REMAINING_BANDWIDTH_THRESHOLD: i64 = 1024 * 1024;
|
||||
/// Specifies the number of tickets in each issued ticketbook.
|
||||
pub const TICKETBOOK_SIZE: u64 = 50;
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum TicketbookType {
|
||||
#[default]
|
||||
MixnetEntry = 0,
|
||||
MixnetExit = 1,
|
||||
WireguardEntry = 2,
|
||||
WireguardExit = 3,
|
||||
}
|
||||
|
||||
impl TicketbookType {
|
||||
pub const WIREGUARD_ENTRY_TICKET_SIZE: u64 = 500 * 1024 * 1024; // 500 MB
|
||||
|
||||
// TBD:
|
||||
pub const WIREGUARD_EXIT_TICKET_SIZE: u64 = Self::WIREGUARD_ENTRY_TICKET_SIZE;
|
||||
pub const MIXNET_ENTRY_TICKET_SIZE: u64 = Self::WIREGUARD_ENTRY_TICKET_SIZE;
|
||||
pub const MIXNET_EXIT_TICKET_SIZE: u64 = Self::WIREGUARD_ENTRY_TICKET_SIZE;
|
||||
|
||||
/// How much bandwidth (in bytes) one ticket can grant
|
||||
pub const fn bandwidth_value(&self) -> u64 {
|
||||
match self {
|
||||
TicketbookType::MixnetEntry => Self::MIXNET_ENTRY_TICKET_SIZE,
|
||||
TicketbookType::MixnetExit => Self::MIXNET_EXIT_TICKET_SIZE,
|
||||
TicketbookType::WireguardEntry => Self::WIREGUARD_ENTRY_TICKET_SIZE,
|
||||
TicketbookType::WireguardExit => Self::WIREGUARD_EXIT_TICKET_SIZE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constants for bloom filter for double spending detection
|
||||
//Chosen for FP of
|
||||
//Calculator at https://hur.st/bloomfilter/
|
||||
pub const ECASH_DS_BLOOMFILTER_PARAMS: BloomfilterParameters = BloomfilterParameters {
|
||||
num_hashes: 13,
|
||||
bitmap_size: 250_000,
|
||||
num_hashes: 10,
|
||||
bitmap_size: 1_500_000_000,
|
||||
sip_keys: [
|
||||
(12345678910111213141, 1415926535897932384),
|
||||
(7182818284590452353, 3571113171923293137),
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::var_names;
|
||||
use crate::var_names::{DEPRECATED_API_VALIDATOR, DEPRECATED_NYMD_VALIDATOR, NYM_API, NYXD};
|
||||
use std::path::Path;
|
||||
|
||||
fn fix_deprecated_environmental_variables() {
|
||||
// if we're using the outdated environmental variables, set the updated ones to preserve compatibility
|
||||
if let Ok(nyxd) = std::env::var(DEPRECATED_NYMD_VALIDATOR) {
|
||||
if std::env::var(NYXD).is_err() {
|
||||
std::env::set_var(NYXD, nyxd)
|
||||
}
|
||||
}
|
||||
if let Ok(nym_apis) = std::env::var(DEPRECATED_API_VALIDATOR) {
|
||||
if std::env::var(NYM_API).is_err() {
|
||||
std::env::set_var(NYM_API, nym_apis)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the variables from the file and log what the corresponding values in the environment are.
|
||||
fn print_env_vars_with_keys_in_file<P: AsRef<Path> + Copy>(config_env_file: P) {
|
||||
let items = dotenvy::from_path_iter(config_env_file)
|
||||
.expect("Invalid path to environment configuration file");
|
||||
for item in items {
|
||||
let (key, val) = item.expect("Invalid item in environment configuration file");
|
||||
log::debug!("{}: {}", key, val);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_env<P: AsRef<Path>>(config_env_file: Option<P>) {
|
||||
match std::env::var(var_names::CONFIGURED) {
|
||||
// if the configuration is not already set in the env vars
|
||||
Err(std::env::VarError::NotPresent) => {
|
||||
if let Some(config_env_file) = &config_env_file {
|
||||
log::debug!(
|
||||
"Loading environment variables from {:?}",
|
||||
config_env_file.as_ref()
|
||||
);
|
||||
dotenvy::from_path(config_env_file)
|
||||
.expect("Invalid path to environment configuration file");
|
||||
fix_deprecated_environmental_variables();
|
||||
} else {
|
||||
// if nothing is set, the use mainnet defaults
|
||||
// if the user has not set `CONFIGURED`, then even if they set any of the env variables,
|
||||
// overwrite them
|
||||
log::debug!("Loading mainnet defaults");
|
||||
crate::mainnet::export_to_env();
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
log::debug!("Environment variables already set. Using them");
|
||||
crate::mainnet::export_to_env()
|
||||
}
|
||||
_ => {
|
||||
fix_deprecated_environmental_variables();
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't explicitly defined any of the constants, fallback to defaults
|
||||
crate::mainnet::export_to_env_if_not_set();
|
||||
|
||||
if let Some(config_env_file) = &config_env_file {
|
||||
print_env_vars_with_keys_in_file(config_env_file);
|
||||
}
|
||||
}
|
||||
@@ -1,471 +1,23 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::var_names::{DEPRECATED_API_VALIDATOR, DEPRECATED_NYMD_VALIDATOR, NYM_API, NYXD};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
env::{var, VarError},
|
||||
ffi::OsStr,
|
||||
ops::Not,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
pub mod constants;
|
||||
pub mod ecash;
|
||||
|
||||
#[cfg(all(feature = "env", feature = "network"))]
|
||||
pub mod env_setup;
|
||||
pub mod mainnet;
|
||||
#[cfg(feature = "network")]
|
||||
pub mod network;
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
pub mod var_names;
|
||||
|
||||
pub use ecash::*;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct ChainDetails {
|
||||
pub bech32_account_prefix: String,
|
||||
pub mix_denom: DenomDetailsOwned,
|
||||
pub stake_denom: DenomDetailsOwned,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct NymContracts {
|
||||
pub mixnet_contract_address: Option<String>,
|
||||
pub vesting_contract_address: Option<String>,
|
||||
pub ecash_contract_address: Option<String>,
|
||||
pub group_contract_address: Option<String>,
|
||||
pub multisig_contract_address: Option<String>,
|
||||
pub coconut_dkg_contract_address: Option<String>,
|
||||
}
|
||||
|
||||
// I wanted to use the simpler `NetworkDetails` name, but there's a clash
|
||||
// with `NetworkDetails` defined in all.rs...
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct NymNetworkDetails {
|
||||
pub network_name: String,
|
||||
pub chain_details: ChainDetails,
|
||||
pub endpoints: Vec<ValidatorDetails>,
|
||||
pub contracts: NymContracts,
|
||||
pub explorer_api: Option<String>,
|
||||
}
|
||||
|
||||
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
|
||||
impl Default for NymNetworkDetails {
|
||||
fn default() -> Self {
|
||||
NymNetworkDetails::new_mainnet()
|
||||
}
|
||||
}
|
||||
|
||||
impl NymNetworkDetails {
|
||||
pub fn new_empty() -> Self {
|
||||
NymNetworkDetails {
|
||||
network_name: Default::default(),
|
||||
chain_details: ChainDetails {
|
||||
bech32_account_prefix: Default::default(),
|
||||
mix_denom: DenomDetailsOwned {
|
||||
base: Default::default(),
|
||||
display: Default::default(),
|
||||
display_exponent: Default::default(),
|
||||
},
|
||||
stake_denom: DenomDetailsOwned {
|
||||
base: Default::default(),
|
||||
display: Default::default(),
|
||||
display_exponent: Default::default(),
|
||||
},
|
||||
},
|
||||
endpoints: Default::default(),
|
||||
contracts: Default::default(),
|
||||
explorer_api: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_env() -> Self {
|
||||
fn get_optional_env<K: AsRef<OsStr>>(env: K) -> Option<String> {
|
||||
match var(env) {
|
||||
Ok(var) => {
|
||||
if var.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(var)
|
||||
}
|
||||
}
|
||||
Err(VarError::NotPresent) => None,
|
||||
err => panic!("Unable to set: {:?}", err),
|
||||
}
|
||||
}
|
||||
|
||||
NymNetworkDetails::new_empty()
|
||||
.with_network_name(var(var_names::NETWORK_NAME).expect("network name not set"))
|
||||
.with_bech32_account_prefix(
|
||||
var(var_names::BECH32_PREFIX).expect("bech32 prefix not set"),
|
||||
)
|
||||
.with_mix_denom(DenomDetailsOwned {
|
||||
base: var(var_names::MIX_DENOM).expect("mix denomination base not set"),
|
||||
display: var(var_names::MIX_DENOM_DISPLAY)
|
||||
.expect("mix denomination display not set"),
|
||||
display_exponent: var(var_names::DENOMS_EXPONENT)
|
||||
.expect("denomination exponent not set")
|
||||
.parse()
|
||||
.expect("denomination exponent is not u32"),
|
||||
})
|
||||
.with_stake_denom(DenomDetailsOwned {
|
||||
base: var(var_names::STAKE_DENOM).expect("stake denomination base not set"),
|
||||
display: var(var_names::STAKE_DENOM_DISPLAY)
|
||||
.expect("stake denomination display not set"),
|
||||
display_exponent: var(var_names::DENOMS_EXPONENT)
|
||||
.expect("denomination exponent not set")
|
||||
.parse()
|
||||
.expect("denomination exponent is not u32"),
|
||||
})
|
||||
.with_additional_validator_endpoint(ValidatorDetails::new(
|
||||
var(var_names::NYXD).expect("nyxd validator not set"),
|
||||
Some(var(var_names::NYM_API).expect("nym api not set")),
|
||||
get_optional_env(var_names::NYXD_WEBSOCKET),
|
||||
))
|
||||
.with_mixnet_contract(get_optional_env(var_names::MIXNET_CONTRACT_ADDRESS))
|
||||
.with_vesting_contract(get_optional_env(var_names::VESTING_CONTRACT_ADDRESS))
|
||||
.with_ecash_contract(get_optional_env(var_names::ECASH_CONTRACT_ADDRESS))
|
||||
.with_group_contract(get_optional_env(var_names::GROUP_CONTRACT_ADDRESS))
|
||||
.with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS))
|
||||
.with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS))
|
||||
.with_explorer_api(get_optional_env(var_names::EXPLORER_API))
|
||||
}
|
||||
|
||||
pub fn new_mainnet() -> Self {
|
||||
fn parse_optional_str(raw: &str) -> Option<String> {
|
||||
raw.is_empty().not().then(|| raw.into())
|
||||
}
|
||||
|
||||
// Consider caching this process (lazy static)
|
||||
NymNetworkDetails {
|
||||
network_name: mainnet::NETWORK_NAME.into(),
|
||||
chain_details: ChainDetails {
|
||||
bech32_account_prefix: mainnet::BECH32_PREFIX.into(),
|
||||
mix_denom: mainnet::MIX_DENOM.into(),
|
||||
stake_denom: mainnet::STAKE_DENOM.into(),
|
||||
},
|
||||
endpoints: mainnet::validators(),
|
||||
contracts: NymContracts {
|
||||
mixnet_contract_address: parse_optional_str(mainnet::MIXNET_CONTRACT_ADDRESS),
|
||||
vesting_contract_address: parse_optional_str(mainnet::VESTING_CONTRACT_ADDRESS),
|
||||
ecash_contract_address: parse_optional_str(mainnet::ECASH_CONTRACT_ADDRESS),
|
||||
group_contract_address: parse_optional_str(mainnet::GROUP_CONTRACT_ADDRESS),
|
||||
multisig_contract_address: parse_optional_str(mainnet::MULTISIG_CONTRACT_ADDRESS),
|
||||
coconut_dkg_contract_address: parse_optional_str(
|
||||
mainnet::COCONUT_DKG_CONTRACT_ADDRESS,
|
||||
),
|
||||
},
|
||||
explorer_api: parse_optional_str(mainnet::EXPLORER_API),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_gas_price_amount(&self) -> f64 {
|
||||
GAS_PRICE_AMOUNT
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_network_name(mut self, network_name: String) -> Self {
|
||||
self.network_name = network_name;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_chain_details(mut self, chain_details: ChainDetails) -> Self {
|
||||
self.chain_details = chain_details;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_bech32_account_prefix<S: Into<String>>(mut self, prefix: S) -> Self {
|
||||
self.chain_details.bech32_account_prefix = prefix.into();
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_mix_denom(mut self, mix_denom: DenomDetailsOwned) -> Self {
|
||||
self.chain_details.mix_denom = mix_denom;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_stake_denom(mut self, stake_denom: DenomDetailsOwned) -> Self {
|
||||
self.chain_details.stake_denom = stake_denom;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_base_mix_denom<S: Into<String>>(mut self, base_mix_denom: S) -> Self {
|
||||
self.chain_details.mix_denom = DenomDetailsOwned::base_only(base_mix_denom.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_base_stake_denom<S: Into<String>>(mut self, base_stake_denom: S) -> Self {
|
||||
self.chain_details.stake_denom = DenomDetailsOwned::base_only(base_stake_denom.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_additional_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self {
|
||||
self.endpoints.push(endpoint);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self {
|
||||
self.endpoints = vec![endpoint];
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_contracts(mut self, contracts: NymContracts) -> Self {
|
||||
self.contracts = contracts;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_mixnet_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.mixnet_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_vesting_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.vesting_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_ecash_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.ecash_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_group_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.group_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_multisig_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.multisig_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_coconut_dkg_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.coconut_dkg_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_explorer_api<S: Into<String>>(mut self, endpoint: Option<S>) -> Self {
|
||||
self.explorer_api = endpoint.map(Into::into);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct DenomDetails {
|
||||
pub base: &'static str,
|
||||
pub display: &'static str,
|
||||
// i.e. display_amount * 10^display_exponent = base_amount
|
||||
pub display_exponent: u32,
|
||||
}
|
||||
|
||||
impl DenomDetails {
|
||||
pub const fn new(base: &'static str, display: &'static str, display_exponent: u32) -> Self {
|
||||
DenomDetails {
|
||||
base,
|
||||
display,
|
||||
display_exponent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, JsonSchema)]
|
||||
pub struct DenomDetailsOwned {
|
||||
pub base: String,
|
||||
pub display: String,
|
||||
// i.e. display_amount * 10^display_exponent = base_amount
|
||||
pub display_exponent: u32,
|
||||
}
|
||||
|
||||
impl From<DenomDetails> for DenomDetailsOwned {
|
||||
fn from(details: DenomDetails) -> Self {
|
||||
DenomDetailsOwned {
|
||||
base: details.base.to_owned(),
|
||||
display: details.display.to_owned(),
|
||||
display_exponent: details.display_exponent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DenomDetailsOwned {
|
||||
pub fn base_only(base: String) -> Self {
|
||||
DenomDetailsOwned {
|
||||
base: base.clone(),
|
||||
display: base,
|
||||
display_exponent: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct ValidatorDetails {
|
||||
// it is assumed those values are always valid since they're being provided in our defaults file
|
||||
pub nyxd_url: String,
|
||||
//
|
||||
pub websocket_url: Option<String>,
|
||||
|
||||
// Right now api_url is optional as we are not running the api reliably on all validators
|
||||
// however, later on it should be a mandatory field
|
||||
pub api_url: Option<String>,
|
||||
// TODO: I'd argue this one should also have a field like `gas_price` since its a validator-specific setting
|
||||
}
|
||||
|
||||
impl ValidatorDetails {
|
||||
pub fn new<S: Into<String>>(nyxd_url: S, api_url: Option<S>, websocket_url: Option<S>) -> Self {
|
||||
ValidatorDetails {
|
||||
nyxd_url: nyxd_url.into(),
|
||||
websocket_url: websocket_url.map(Into::into),
|
||||
api_url: api_url.map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_nyxd_only<S: Into<String>>(nyxd_url: S) -> Self {
|
||||
ValidatorDetails {
|
||||
nyxd_url: nyxd_url.into(),
|
||||
websocket_url: None,
|
||||
api_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nyxd_url(&self) -> Url {
|
||||
self.nyxd_url
|
||||
.parse()
|
||||
.expect("the provided nyxd url is invalid!")
|
||||
}
|
||||
|
||||
pub fn api_url(&self) -> Option<Url> {
|
||||
self.api_url
|
||||
.as_ref()
|
||||
.map(|url| url.parse().expect("the provided api url is invalid!"))
|
||||
}
|
||||
|
||||
pub fn websocket_url(&self) -> Option<Url> {
|
||||
self.websocket_url
|
||||
.as_ref()
|
||||
.map(|url| url.parse().expect("the provided websocket url is invalid!"))
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_deprecated_environmental_variables() {
|
||||
// if we're using the outdated environmental variables, set the updated ones to preserve compatibility
|
||||
if let Ok(nyxd) = std::env::var(DEPRECATED_NYMD_VALIDATOR) {
|
||||
if std::env::var(NYXD).is_err() {
|
||||
std::env::set_var(NYXD, nyxd)
|
||||
}
|
||||
}
|
||||
if let Ok(nym_apis) = std::env::var(DEPRECATED_API_VALIDATOR) {
|
||||
if std::env::var(NYM_API).is_err() {
|
||||
std::env::set_var(NYM_API, nym_apis)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the variables from the file and log what the corresponding values in the environment are.
|
||||
fn print_env_vars_with_keys_in_file<P: AsRef<Path> + Copy>(config_env_file: P) {
|
||||
let items = dotenvy::from_path_iter(config_env_file)
|
||||
.expect("Invalid path to environment configuration file");
|
||||
for item in items {
|
||||
let (key, val) = item.expect("Invalid item in environment configuration file");
|
||||
log::debug!("{}: {}", key, val);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_env<P: AsRef<Path>>(config_env_file: Option<P>) {
|
||||
match std::env::var(var_names::CONFIGURED) {
|
||||
// if the configuration is not already set in the env vars
|
||||
Err(std::env::VarError::NotPresent) => {
|
||||
if let Some(config_env_file) = &config_env_file {
|
||||
log::debug!(
|
||||
"Loading environment variables from {:?}",
|
||||
config_env_file.as_ref()
|
||||
);
|
||||
dotenvy::from_path(config_env_file)
|
||||
.expect("Invalid path to environment configuration file");
|
||||
fix_deprecated_environmental_variables();
|
||||
} else {
|
||||
// if nothing is set, the use mainnet defaults
|
||||
// if the user has not set `CONFIGURED`, then even if they set any of the env variables,
|
||||
// overwrite them
|
||||
log::debug!("Loading mainnet defaults");
|
||||
crate::mainnet::export_to_env();
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
log::debug!("Environment variables already set. Using them");
|
||||
crate::mainnet::export_to_env()
|
||||
}
|
||||
_ => {
|
||||
fix_deprecated_environmental_variables();
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't explicitly defined any of the constants, fallback to defaults
|
||||
crate::mainnet::export_to_env_if_not_set();
|
||||
|
||||
if let Some(config_env_file) = &config_env_file {
|
||||
print_env_vars_with_keys_in_file(config_env_file);
|
||||
}
|
||||
}
|
||||
|
||||
/// Defaults Cosmos Hub/ATOM path
|
||||
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
||||
// as set by validators in their configs
|
||||
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
||||
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
||||
|
||||
pub const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
|
||||
|
||||
// 'GATEWAY'
|
||||
pub const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000;
|
||||
|
||||
// 'MIXNODE'
|
||||
pub const DEFAULT_VERLOC_LISTENING_PORT: u16 = 1790;
|
||||
pub const DEFAULT_HTTP_API_LISTENING_PORT: u16 = 8000;
|
||||
|
||||
// 'CLIENT'
|
||||
pub const DEFAULT_WEBSOCKET_LISTENING_PORT: u16 = 1977;
|
||||
|
||||
// 'SOCKS5' CLIENT
|
||||
pub const DEFAULT_SOCKS5_LISTENING_PORT: u16 = 1080;
|
||||
|
||||
// NYM-API
|
||||
pub const DEFAULT_NYM_API_PORT: u16 = 8080;
|
||||
|
||||
pub const NYM_API_VERSION: &str = "v1";
|
||||
|
||||
// NYM-NODE
|
||||
pub const DEFAULT_NYM_NODE_HTTP_PORT: u16 = 8080;
|
||||
|
||||
// REWARDING
|
||||
|
||||
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have reliable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
|
||||
// pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$
|
||||
// pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 55_556; // 40$/1hr at 1 Nym == 1$
|
||||
// pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 9259; // 40$/1hr/6 at 1 Nym == 1$
|
||||
|
||||
// TODO: is there a way to get this from the chain
|
||||
pub const TOTAL_SUPPLY: u128 = 1_000_000_000_000_000;
|
||||
|
||||
pub const DEFAULT_PROFIT_MARGIN: u8 = 10;
|
||||
|
||||
// WIREGUARD
|
||||
pub const WG_PORT: u16 = 51822;
|
||||
|
||||
// The interface used to route traffic
|
||||
pub const WG_TUN_BASE_NAME: &str = "nymwg";
|
||||
pub const WG_TUN_DEVICE_ADDRESS: &str = "10.1.0.1";
|
||||
pub const WG_TUN_DEVICE_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(10, 1, 0, 1));
|
||||
pub const WG_TUN_DEVICE_NETMASK: &str = "255.255.255.0";
|
||||
// re-export everything to not break existing imports
|
||||
pub use constants::*;
|
||||
#[cfg(all(feature = "env", feature = "network"))]
|
||||
pub use env_setup::*;
|
||||
#[cfg(feature = "network")]
|
||||
pub use network::*;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::var_names;
|
||||
#[cfg(feature = "network")]
|
||||
use crate::{DenomDetails, ValidatorDetails};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub const NETWORK_NAME: &str = "mainnet";
|
||||
|
||||
pub const BECH32_PREFIX: &str = "n";
|
||||
|
||||
#[cfg(feature = "network")]
|
||||
pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6);
|
||||
#[cfg(feature = "network")]
|
||||
pub const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
|
||||
|
||||
pub const MIXNET_CONTRACT_ADDRESS: &str =
|
||||
@@ -36,6 +37,7 @@ pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/";
|
||||
pub const EXIT_POLICY_URL: &str =
|
||||
"https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
|
||||
|
||||
#[cfg(feature = "network")]
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
NYXD_URL,
|
||||
@@ -44,23 +46,28 @@ pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
)]
|
||||
}
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
const DEFAULT_SUFFIX: &str = "_MAINNET_DEFAULT";
|
||||
|
||||
#[cfg(all(feature = "env", feature = "network"))]
|
||||
fn set_var_to_default(var: &str, value: &str) {
|
||||
std::env::set_var(var, value);
|
||||
std::env::set_var(format!("{var}{DEFAULT_SUFFIX}"), "1")
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "env", feature = "network"))]
|
||||
fn set_var_conditionally_to_default(var: &str, value: &str) {
|
||||
if std::env::var(var).is_err() {
|
||||
set_var_to_default(var, value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
pub fn uses_default(var: &str) -> bool {
|
||||
std::env::var(format!("{var}{DEFAULT_SUFFIX}")).is_ok()
|
||||
}
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
pub fn read_var_if_not_default(var: &str) -> Option<String> {
|
||||
if uses_default(var) {
|
||||
None
|
||||
@@ -69,13 +76,19 @@ pub fn read_var_if_not_default(var: &str) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_parsed_var_if_not_default<T: FromStr>(var: &str) -> Option<Result<T, T::Err>> {
|
||||
#[cfg(feature = "env")]
|
||||
pub fn read_parsed_var_if_not_default<T: std::str::FromStr>(
|
||||
var: &str,
|
||||
) -> Option<Result<T, T::Err>> {
|
||||
read_var_if_not_default(var)
|
||||
.as_deref()
|
||||
.map(FromStr::from_str)
|
||||
.map(std::str::FromStr::from_str)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "env", feature = "network"))]
|
||||
pub fn export_to_env() {
|
||||
use crate::var_names;
|
||||
|
||||
set_var_to_default(var_names::CONFIGURED, "true");
|
||||
set_var_to_default(var_names::NETWORK_NAME, NETWORK_NAME);
|
||||
set_var_to_default(var_names::BECH32_PREFIX, BECH32_PREFIX);
|
||||
@@ -113,7 +126,10 @@ pub fn export_to_env() {
|
||||
set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "env", feature = "network"))]
|
||||
pub fn export_to_env_if_not_set() {
|
||||
use crate::var_names;
|
||||
|
||||
set_var_conditionally_to_default(var_names::CONFIGURED, "true");
|
||||
set_var_conditionally_to_default(var_names::NETWORK_NAME, NETWORK_NAME);
|
||||
set_var_conditionally_to_default(var_names::BECH32_PREFIX, BECH32_PREFIX);
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{mainnet, GAS_PRICE_AMOUNT};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Not;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct ChainDetails {
|
||||
pub bech32_account_prefix: String,
|
||||
pub mix_denom: DenomDetailsOwned,
|
||||
pub stake_denom: DenomDetailsOwned,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct NymContracts {
|
||||
pub mixnet_contract_address: Option<String>,
|
||||
pub vesting_contract_address: Option<String>,
|
||||
pub ecash_contract_address: Option<String>,
|
||||
pub group_contract_address: Option<String>,
|
||||
pub multisig_contract_address: Option<String>,
|
||||
pub coconut_dkg_contract_address: Option<String>,
|
||||
}
|
||||
|
||||
// I wanted to use the simpler `NetworkDetails` name, but there's a clash
|
||||
// with `NetworkDetails` defined in all.rs...
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct NymNetworkDetails {
|
||||
pub network_name: String,
|
||||
pub chain_details: ChainDetails,
|
||||
pub endpoints: Vec<ValidatorDetails>,
|
||||
pub contracts: NymContracts,
|
||||
pub explorer_api: Option<String>,
|
||||
}
|
||||
|
||||
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
|
||||
impl Default for NymNetworkDetails {
|
||||
fn default() -> Self {
|
||||
NymNetworkDetails::new_mainnet()
|
||||
}
|
||||
}
|
||||
|
||||
impl NymNetworkDetails {
|
||||
pub fn new_empty() -> Self {
|
||||
NymNetworkDetails {
|
||||
network_name: Default::default(),
|
||||
chain_details: ChainDetails {
|
||||
bech32_account_prefix: Default::default(),
|
||||
mix_denom: DenomDetailsOwned {
|
||||
base: Default::default(),
|
||||
display: Default::default(),
|
||||
display_exponent: Default::default(),
|
||||
},
|
||||
stake_denom: DenomDetailsOwned {
|
||||
base: Default::default(),
|
||||
display: Default::default(),
|
||||
display_exponent: Default::default(),
|
||||
},
|
||||
},
|
||||
endpoints: Default::default(),
|
||||
contracts: Default::default(),
|
||||
explorer_api: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "env")]
|
||||
pub fn new_from_env() -> Self {
|
||||
use crate::var_names;
|
||||
use std::env::{var, VarError};
|
||||
use std::ffi::OsStr;
|
||||
|
||||
fn get_optional_env<K: AsRef<OsStr>>(env: K) -> Option<String> {
|
||||
match var(env) {
|
||||
Ok(var) => {
|
||||
if var.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(var)
|
||||
}
|
||||
}
|
||||
Err(VarError::NotPresent) => None,
|
||||
err => panic!("Unable to set: {:?}", err),
|
||||
}
|
||||
}
|
||||
|
||||
NymNetworkDetails::new_empty()
|
||||
.with_network_name(var(var_names::NETWORK_NAME).expect("network name not set"))
|
||||
.with_bech32_account_prefix(
|
||||
var(var_names::BECH32_PREFIX).expect("bech32 prefix not set"),
|
||||
)
|
||||
.with_mix_denom(DenomDetailsOwned {
|
||||
base: var(var_names::MIX_DENOM).expect("mix denomination base not set"),
|
||||
display: var(var_names::MIX_DENOM_DISPLAY)
|
||||
.expect("mix denomination display not set"),
|
||||
display_exponent: var(var_names::DENOMS_EXPONENT)
|
||||
.expect("denomination exponent not set")
|
||||
.parse()
|
||||
.expect("denomination exponent is not u32"),
|
||||
})
|
||||
.with_stake_denom(DenomDetailsOwned {
|
||||
base: var(var_names::STAKE_DENOM).expect("stake denomination base not set"),
|
||||
display: var(var_names::STAKE_DENOM_DISPLAY)
|
||||
.expect("stake denomination display not set"),
|
||||
display_exponent: var(var_names::DENOMS_EXPONENT)
|
||||
.expect("denomination exponent not set")
|
||||
.parse()
|
||||
.expect("denomination exponent is not u32"),
|
||||
})
|
||||
.with_additional_validator_endpoint(ValidatorDetails::new(
|
||||
var(var_names::NYXD).expect("nyxd validator not set"),
|
||||
Some(var(var_names::NYM_API).expect("nym api not set")),
|
||||
get_optional_env(var_names::NYXD_WEBSOCKET),
|
||||
))
|
||||
.with_mixnet_contract(get_optional_env(var_names::MIXNET_CONTRACT_ADDRESS))
|
||||
.with_vesting_contract(get_optional_env(var_names::VESTING_CONTRACT_ADDRESS))
|
||||
.with_ecash_contract(get_optional_env(var_names::ECASH_CONTRACT_ADDRESS))
|
||||
.with_group_contract(get_optional_env(var_names::GROUP_CONTRACT_ADDRESS))
|
||||
.with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS))
|
||||
.with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS))
|
||||
.with_explorer_api(get_optional_env(var_names::EXPLORER_API))
|
||||
}
|
||||
|
||||
pub fn new_mainnet() -> Self {
|
||||
fn parse_optional_str(raw: &str) -> Option<String> {
|
||||
raw.is_empty().not().then(|| raw.into())
|
||||
}
|
||||
|
||||
// Consider caching this process (lazy static)
|
||||
NymNetworkDetails {
|
||||
network_name: mainnet::NETWORK_NAME.into(),
|
||||
chain_details: ChainDetails {
|
||||
bech32_account_prefix: mainnet::BECH32_PREFIX.into(),
|
||||
mix_denom: mainnet::MIX_DENOM.into(),
|
||||
stake_denom: mainnet::STAKE_DENOM.into(),
|
||||
},
|
||||
endpoints: mainnet::validators(),
|
||||
contracts: NymContracts {
|
||||
mixnet_contract_address: parse_optional_str(mainnet::MIXNET_CONTRACT_ADDRESS),
|
||||
vesting_contract_address: parse_optional_str(mainnet::VESTING_CONTRACT_ADDRESS),
|
||||
ecash_contract_address: parse_optional_str(mainnet::ECASH_CONTRACT_ADDRESS),
|
||||
group_contract_address: parse_optional_str(mainnet::GROUP_CONTRACT_ADDRESS),
|
||||
multisig_contract_address: parse_optional_str(mainnet::MULTISIG_CONTRACT_ADDRESS),
|
||||
coconut_dkg_contract_address: parse_optional_str(
|
||||
mainnet::COCONUT_DKG_CONTRACT_ADDRESS,
|
||||
),
|
||||
},
|
||||
explorer_api: parse_optional_str(mainnet::EXPLORER_API),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_gas_price_amount(&self) -> f64 {
|
||||
GAS_PRICE_AMOUNT
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_network_name(mut self, network_name: String) -> Self {
|
||||
self.network_name = network_name;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_chain_details(mut self, chain_details: ChainDetails) -> Self {
|
||||
self.chain_details = chain_details;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_bech32_account_prefix<S: Into<String>>(mut self, prefix: S) -> Self {
|
||||
self.chain_details.bech32_account_prefix = prefix.into();
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_mix_denom(mut self, mix_denom: DenomDetailsOwned) -> Self {
|
||||
self.chain_details.mix_denom = mix_denom;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_stake_denom(mut self, stake_denom: DenomDetailsOwned) -> Self {
|
||||
self.chain_details.stake_denom = stake_denom;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_base_mix_denom<S: Into<String>>(mut self, base_mix_denom: S) -> Self {
|
||||
self.chain_details.mix_denom = DenomDetailsOwned::base_only(base_mix_denom.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_base_stake_denom<S: Into<String>>(mut self, base_stake_denom: S) -> Self {
|
||||
self.chain_details.stake_denom = DenomDetailsOwned::base_only(base_stake_denom.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_additional_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self {
|
||||
self.endpoints.push(endpoint);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self {
|
||||
self.endpoints = vec![endpoint];
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_contracts(mut self, contracts: NymContracts) -> Self {
|
||||
self.contracts = contracts;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_mixnet_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.mixnet_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_vesting_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.vesting_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_ecash_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.ecash_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_group_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.group_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_multisig_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.multisig_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_coconut_dkg_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
|
||||
self.contracts.coconut_dkg_contract_address = contract.map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_explorer_api<S: Into<String>>(mut self, endpoint: Option<S>) -> Self {
|
||||
self.explorer_api = endpoint.map(Into::into);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct DenomDetails {
|
||||
pub base: &'static str,
|
||||
pub display: &'static str,
|
||||
// i.e. display_amount * 10^display_exponent = base_amount
|
||||
pub display_exponent: u32,
|
||||
}
|
||||
|
||||
impl DenomDetails {
|
||||
pub const fn new(base: &'static str, display: &'static str, display_exponent: u32) -> Self {
|
||||
DenomDetails {
|
||||
base,
|
||||
display,
|
||||
display_exponent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, JsonSchema)]
|
||||
pub struct DenomDetailsOwned {
|
||||
pub base: String,
|
||||
pub display: String,
|
||||
// i.e. display_amount * 10^display_exponent = base_amount
|
||||
pub display_exponent: u32,
|
||||
}
|
||||
|
||||
impl From<DenomDetails> for DenomDetailsOwned {
|
||||
fn from(details: DenomDetails) -> Self {
|
||||
DenomDetailsOwned {
|
||||
base: details.base.to_owned(),
|
||||
display: details.display.to_owned(),
|
||||
display_exponent: details.display_exponent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DenomDetailsOwned {
|
||||
pub fn base_only(base: String) -> Self {
|
||||
DenomDetailsOwned {
|
||||
base: base.clone(),
|
||||
display: base,
|
||||
display_exponent: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct ValidatorDetails {
|
||||
// it is assumed those values are always valid since they're being provided in our defaults file
|
||||
pub nyxd_url: String,
|
||||
//
|
||||
pub websocket_url: Option<String>,
|
||||
|
||||
// Right now api_url is optional as we are not running the api reliably on all validators
|
||||
// however, later on it should be a mandatory field
|
||||
pub api_url: Option<String>,
|
||||
// TODO: I'd argue this one should also have a field like `gas_price` since its a validator-specific setting
|
||||
}
|
||||
|
||||
impl ValidatorDetails {
|
||||
pub fn new<S: Into<String>>(nyxd_url: S, api_url: Option<S>, websocket_url: Option<S>) -> Self {
|
||||
ValidatorDetails {
|
||||
nyxd_url: nyxd_url.into(),
|
||||
websocket_url: websocket_url.map(Into::into),
|
||||
api_url: api_url.map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_nyxd_only<S: Into<String>>(nyxd_url: S) -> Self {
|
||||
ValidatorDetails {
|
||||
nyxd_url: nyxd_url.into(),
|
||||
websocket_url: None,
|
||||
api_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nyxd_url(&self) -> Url {
|
||||
self.nyxd_url
|
||||
.parse()
|
||||
.expect("the provided nyxd url is invalid!")
|
||||
}
|
||||
|
||||
pub fn api_url(&self) -> Option<Url> {
|
||||
self.api_url
|
||||
.as_ref()
|
||||
.map(|url| url.parse().expect("the provided api url is invalid!"))
|
||||
}
|
||||
|
||||
pub fn websocket_url(&self) -> Option<Url> {
|
||||
self.websocket_url
|
||||
.as_ref()
|
||||
.map(|url| url.parse().expect("the provided websocket url is invalid!"))
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ ff = { workspace = true }
|
||||
group = { workspace = true }
|
||||
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
nym-network-defaults = { path = "../network-defaults", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bls12_381::Scalar;
|
||||
use nym_network_defaults::ecash::TICKETBOOK_VALIDITY_DAYS;
|
||||
use nym_network_defaults::TICKETBOOK_SIZE;
|
||||
|
||||
pub const PUBLIC_ATTRIBUTES_LEN: usize = 1; //expiration date
|
||||
pub const PRIVATE_ATTRIBUTES_LEN: usize = 2; //user and wallet secret
|
||||
pub const ATTRIBUTES_LEN: usize = PUBLIC_ATTRIBUTES_LEN + PRIVATE_ATTRIBUTES_LEN; // number of attributes encoded in a single zk-nym credential
|
||||
|
||||
pub const CRED_VALIDITY_PERIOD_DAYS: u64 = 30;
|
||||
pub const CRED_VALIDITY_PERIOD_DAYS: u64 = TICKETBOOK_VALIDITY_DAYS;
|
||||
|
||||
pub(crate) const SECONDS_PER_DAY: u64 = 86400;
|
||||
|
||||
/// Total number of tickets in each issued ticket book.
|
||||
pub const NB_TICKETS: u64 = 100;
|
||||
pub const NB_TICKETS: u64 = TICKETBOOK_SIZE;
|
||||
|
||||
pub const TYPE_EXP: Scalar = Scalar::from_raw([
|
||||
u64::from_le_bytes(*b"ZKNYMEXP"),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_network_defaults::TicketbookType;
|
||||
use std::num::ParseIntError;
|
||||
use thiserror::Error;
|
||||
use time::error::ComponentRange;
|
||||
@@ -41,9 +42,9 @@ impl Bandwidth {
|
||||
Bandwidth { value }
|
||||
}
|
||||
|
||||
pub fn ticket_amount() -> Self {
|
||||
pub fn ticket_amount(typ: TicketbookType) -> Self {
|
||||
Bandwidth {
|
||||
value: nym_network_defaults::TICKET_BANDWIDTH_VALUE,
|
||||
value: typ.bandwidth_value(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -427,7 +427,7 @@ where
|
||||
// TODO: double storing?
|
||||
// self.store_spent_credential(serial_number_bs58).await?;
|
||||
|
||||
let bandwidth = Bandwidth::ticket_amount();
|
||||
let bandwidth = Bandwidth::ticket_amount(Default::default());
|
||||
|
||||
self.increase_bandwidth(bandwidth, spend_date).await?;
|
||||
|
||||
|
||||
+2
-2
@@ -380,8 +380,8 @@ where
|
||||
async fn revoke_ticket_bandwidth(&self, ticket_id: i64) -> Result<(), EcashTicketError> {
|
||||
warn!("revoking bandwidth associated with ticket {ticket_id} since it failed verification");
|
||||
|
||||
let bytes_to_revoke =
|
||||
Bandwidth::ticket_amount().value() as f32 * self.config.revocation_bandwidth_penalty;
|
||||
let bytes_to_revoke = Bandwidth::ticket_amount(Default::default()).value() as f32
|
||||
* self.config.revocation_bandwidth_penalty;
|
||||
let to_revoke_bi2 = bibytes2(bytes_to_revoke as f64);
|
||||
|
||||
info!(to_revoke_bi2);
|
||||
|
||||
@@ -13,9 +13,9 @@ use futures::task::Context;
|
||||
use futures::{Future, Stream};
|
||||
use log::{debug, info, trace, warn};
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_config::defaults::REMAINING_BANDWIDTH_THRESHOLD;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH};
|
||||
use nym_gateway_client::client::config::GatewayClientConfig;
|
||||
use nym_gateway_client::client::GatewayConfig;
|
||||
use nym_gateway_client::error::GatewayClientError;
|
||||
use nym_gateway_client::{
|
||||
@@ -205,15 +205,16 @@ impl PacketSender {
|
||||
);
|
||||
|
||||
let gateway_client = GatewayClient::new(
|
||||
GatewayClientConfig::new_default()
|
||||
.with_disabled_credentials_mode(fresh_gateway_client_data.disabled_credentials_mode)
|
||||
.with_response_timeout(fresh_gateway_client_data.gateway_response_timeout),
|
||||
config,
|
||||
Arc::clone(&fresh_gateway_client_data.local_identity),
|
||||
None,
|
||||
gateway_packet_router,
|
||||
Some(fresh_gateway_client_data.bandwidth_controller.clone()),
|
||||
task_client,
|
||||
)
|
||||
.with_disabled_credentials_mode(fresh_gateway_client_data.disabled_credentials_mode)
|
||||
.with_response_timeout(fresh_gateway_client_data.gateway_response_timeout);
|
||||
);
|
||||
|
||||
(
|
||||
GatewayClientHandle::new(gateway_client),
|
||||
@@ -325,9 +326,9 @@ impl PacketSender {
|
||||
async fn check_remaining_bandwidth(
|
||||
client: &mut GatewayClient<nyxd::Client, PersistentStorage>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if client.remaining_bandwidth() < REMAINING_BANDWIDTH_THRESHOLD {
|
||||
if client.remaining_bandwidth() < client.cfg.bandwidth.remaining_bandwidth_threshold {
|
||||
Err(GatewayClientError::NotEnoughBandwidth(
|
||||
REMAINING_BANDWIDTH_THRESHOLD,
|
||||
client.cfg.bandwidth.remaining_bandwidth_threshold,
|
||||
client.remaining_bandwidth(),
|
||||
))
|
||||
} else {
|
||||
|
||||
Generated
+1
-4
@@ -3159,6 +3159,7 @@ dependencies = [
|
||||
"ff",
|
||||
"group",
|
||||
"itertools 0.12.1",
|
||||
"nym-network-defaults",
|
||||
"nym-pemstore",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@@ -3318,14 +3319,10 @@ dependencies = [
|
||||
name = "nym-network-defaults"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dotenvy",
|
||||
"hex-literal",
|
||||
"log",
|
||||
"once_cell",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user