diff --git a/Cargo.lock b/Cargo.lock index 5b6bcab1c7..88187f611b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5213,15 +5213,13 @@ dependencies = [ "futures", "gloo-timers", "http-body-util", - "humantime-serde", + "humantime", "hyper 1.6.0", "hyper-util", - "log", "nym-bandwidth-controller", "nym-client-core-config-types", "nym-client-core-gateways-storage", "nym-client-core-surb-storage", - "nym-config 0.1.0", "nym-credential-storage", "nym-credentials-interface 0.1.0", "nym-crypto 0.4.0", @@ -5230,7 +5228,6 @@ dependencies = [ "nym-gateway-requests", "nym-http-api-client 0.1.0", "nym-id", - "nym-metrics 0.1.0", "nym-mixnet-client", "nym-network-defaults 0.1.0", "nym-nonexhaustive-delayqueue", @@ -5283,7 +5280,6 @@ version = "0.1.0" dependencies = [ "async-trait", "cosmrs", - "log", "nym-crypto 0.4.0", "nym-gateway-requests", "serde", @@ -5291,6 +5287,7 @@ dependencies = [ "thiserror 2.0.12", "time", "tokio", + "tracing", "url", "zeroize", ] @@ -5301,7 +5298,6 @@ version = "0.1.0" dependencies = [ "async-trait", "dashmap", - "log", "nym-crypto 0.4.0", "nym-sphinx 0.1.0", "nym-task 0.1.0", @@ -5309,6 +5305,7 @@ dependencies = [ "thiserror 2.0.12", "time", "tokio", + "tracing", ] [[package]] @@ -6099,6 +6096,7 @@ dependencies = [ "futures", "mime", "serde", + "serde_json", "serde_yaml", "subtle 2.6.1", "time", @@ -6950,6 +6948,7 @@ dependencies = [ "tap", "tempfile", "thiserror 2.0.12", + "time", "tokio", "tokio-stream", "tokio-util", @@ -7600,17 +7599,16 @@ version = "0.1.0" dependencies = [ "async-trait", "nym-api-requests 0.1.0", - "nym-config 0.1.0", "nym-crypto 0.4.0", "nym-mixnet-contract-common 0.6.0", "nym-sphinx-addressing 0.1.0", - "nym-sphinx-routing 0.1.0", "nym-sphinx-types 0.2.0", "rand 0.8.5", "reqwest 0.12.15", "serde", "serde_json", "thiserror 2.0.12", + "time", "tracing", "tsify", "wasm-bindgen", @@ -10721,6 +10719,7 @@ dependencies = [ "console", "cw-utils", "dkg-bypass-contract", + "humantime", "indicatif", "nym-bin-common 0.6.0", "nym-coconut-dkg-common 0.1.0", diff --git a/clients/native/src/websocket/handler.rs b/clients/native/src/websocket/handler.rs index 2df449385d..761e2b9531 100644 --- a/clients/native/src/websocket/handler.rs +++ b/clients/native/src/websocket/handler.rs @@ -318,7 +318,7 @@ impl Handler { async fn handle_text_message(&mut self, msg: String) -> Option { debug!("Handling text message request"); - trace!("Content: {:?}", msg); + trace!("Content: {msg:?}"); self.received_response_type = ReceivedResponseType::Text; let client_request = ClientRequest::try_from_text(msg); diff --git a/clients/native/src/websocket/listener.rs b/clients/native/src/websocket/listener.rs index cc36198d40..a1b430a930 100644 --- a/clients/native/src/websocket/listener.rs +++ b/clients/native/src/websocket/listener.rs @@ -68,9 +68,9 @@ impl Listener { new_conn = tcp_listener.accept() => { match new_conn { Ok((mut socket, remote_addr)) => { - debug!("Received connection from {:?}", remote_addr); + debug!("Received connection from {remote_addr:?}"); if self.state.is_connected() { - warn!("Tried to open a duplicate websocket connection. The request came from {}", remote_addr); + warn!("Tried to open a duplicate websocket connection. The request came from {remote_addr}"); // if we've already got a connection, don't allow another one // while we only ever want to accept a single connection, we don't want // to leave clients hanging (and also allow for reconnection if it somehow diff --git a/common/async-file-watcher/src/lib.rs b/common/async-file-watcher/src/lib.rs index 62bb895bcd..0fdeb7da3a 100644 --- a/common/async-file-watcher/src/lib.rs +++ b/common/async-file-watcher/src/lib.rs @@ -137,7 +137,7 @@ impl AsyncFileWatcher { log::error!("the file watcher receiver has been dropped!"); } } else { - log::debug!("will not propagate information about {:?}", event); + log::debug!("will not propagate information about {event:?}"); } } Err(err) => { diff --git a/common/bandwidth-controller/src/event.rs b/common/bandwidth-controller/src/event.rs index ee968dfd93..e5f78228dd 100644 --- a/common/bandwidth-controller/src/event.rs +++ b/common/bandwidth-controller/src/event.rs @@ -11,7 +11,7 @@ impl std::fmt::Display for BandwidthStatusMessage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { BandwidthStatusMessage::RemainingBandwidth(b) => { - write!(f, "remaining bandwidth: {}", b) + write!(f, "remaining bandwidth: {b}") } BandwidthStatusMessage::NoBandwidth => write!(f, "no bandwidth left"), } diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index 5d4f0bbfb7..7cca47759f 100644 --- a/common/client-core/Cargo.toml +++ b/common/client-core/Cargo.toml @@ -15,8 +15,7 @@ bs58 = { workspace = true } clap = { workspace = true, optional = true } comfy-table = { workspace = true, optional = true } futures = { workspace = true } -humantime-serde = { workspace = true } -log = { workspace = true } +humantime = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } serde = { workspace = true, features = ["derive"] } @@ -25,20 +24,18 @@ sha2 = { workspace = true } si-scale = { workspace = true } thiserror = { workspace = true } url = { workspace = true, features = ["serde"] } -tokio = { workspace = true, features = ["macros"] } time = { workspace = true } +tokio = { workspace = true, features = ["sync", "macros"] } tracing = { workspace = true } zeroize = { workspace = true } # internal nym-id = { path = "../nym-id" } nym-bandwidth-controller = { path = "../bandwidth-controller" } -nym-config = { path = "../config" } nym-crypto = { path = "../crypto" } nym-gateway-client = { path = "../client-libs/gateway-client" } nym-gateway-requests = { path = "../gateway-requests" } nym-http-api-client = { path = "../http-api-client" } -nym-metrics = { path = "../nym-metrics" } nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" } nym-sphinx = { path = "../nymsphinx" } nym-statistics-common = { path = "../statistics" } diff --git a/common/client-core/config-types/src/lib.rs b/common/client-core/config-types/src/lib.rs index 81c47b4219..914c5feea0 100644 --- a/common/client-core/config-types/src/lib.rs +++ b/common/client-core/config-types/src/lib.rs @@ -57,9 +57,7 @@ const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500; const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10); const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60); - -// 12 hours -const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60); +const DEFAULT_MAXIMUM_REPLY_SURB_REREQUESTS: usize = 5; // 24 hours const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60); @@ -625,10 +623,9 @@ pub struct ReplySurbs { #[serde(with = "humantime_serde")] pub maximum_reply_surb_drop_waiting_period: Duration, - /// Defines maximum amount of time given reply surb is going to be valid for. - /// This is going to be superseded by key rotation once implemented. - #[serde(with = "humantime_serde")] - pub maximum_reply_surb_age: Duration, + /// Defines maximum number of times the client is going to re-request reply surbs + /// for clearing pending messages before giving up after making no progress. + pub maximum_reply_surbs_rerequests: usize, /// Defines maximum amount of time given reply key is going to be valid for. /// This is going to be superseded by key rotation once implemented. @@ -638,9 +635,6 @@ pub struct ReplySurbs { /// Specifies the number of mixnet hops the packet should go through. If not specified, then /// the default value is used. pub surb_mix_hops: Option, - - /// Specifies if we should reset all the sender tags on startup - pub fresh_sender_tags: bool, } impl Default for ReplySurbs { @@ -655,10 +649,9 @@ impl Default for ReplySurbs { maximum_reply_surb_rerequest_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD, maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, - maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE, + maximum_reply_surbs_rerequests: DEFAULT_MAXIMUM_REPLY_SURB_REREQUESTS, maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE, surb_mix_hops: None, - fresh_sender_tags: false, } } } diff --git a/common/client-core/config-types/src/old/v6.rs b/common/client-core/config-types/src/old/v6.rs index 0f01db2a62..66bf388114 100644 --- a/common/client-core/config-types/src/old/v6.rs +++ b/common/client-core/config-types/src/old/v6.rs @@ -189,14 +189,13 @@ impl From for Config { .debug .reply_surbs .maximum_reply_surb_drop_waiting_period, - maximum_reply_surb_age: value.debug.reply_surbs.maximum_reply_surb_age, maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age, surb_mix_hops: value.debug.reply_surbs.surb_mix_hops, minimum_reply_surb_threshold_buffer: value .debug .reply_surbs .minimum_reply_surb_threshold_buffer, - fresh_sender_tags: value.debug.reply_surbs.fresh_sender_tags, + ..Default::default() }, stats_reporting: StatsReporting { enabled: value.debug.stats_reporting.enabled, diff --git a/common/client-core/gateways-storage/Cargo.toml b/common/client-core/gateways-storage/Cargo.toml index e91c889ed6..ef2a1b5fdf 100644 --- a/common/client-core/gateways-storage/Cargo.toml +++ b/common/client-core/gateways-storage/Cargo.toml @@ -9,11 +9,11 @@ license.workspace = true [dependencies] async-trait.workspace = true cosmrs.workspace = true -log.workspace = true serde = { workspace = true, features = ["derive"] } thiserror.workspace = true time.workspace = true tokio = { workspace = true, features = ["sync"] } +tracing.workspace = true url.workspace = true zeroize = { workspace = true, features = ["zeroize_derive"] } diff --git a/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs b/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs index 101a6a8710..b28dc6e77e 100644 --- a/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs +++ b/common/client-core/gateways-storage/src/backend/fs_backend/manager.rs @@ -7,12 +7,12 @@ use crate::{ RawActiveGateway, RawCustomGatewayDetails, RawRegisteredGateway, RawRemoteGatewayDetails, }, }; -use log::{debug, error}; use sqlx::{ sqlite::{SqliteAutoVacuum, SqliteSynchronous}, ConnectOptions, }; use std::path::Path; +use tracing::{debug, error}; #[derive(Debug, Clone)] pub struct StorageManager { diff --git a/common/client-core/src/cli_helpers/client_add_gateway.rs b/common/client-core/src/cli_helpers/client_add_gateway.rs index 8811aff1d3..5662a25a3d 100644 --- a/common/client-core/src/cli_helpers/client_add_gateway.rs +++ b/common/client-core/src/cli_helpers/client_add_gateway.rs @@ -12,12 +12,12 @@ use crate::{ error::ClientCoreError, init::types::{GatewaySelectionSpecification, GatewaySetup}, }; -use log::info; use nym_client_core_gateways_storage::GatewayDetails; use nym_crypto::asymmetric::ed25519; use nym_topology::NymTopology; use nym_validator_client::UserAgent; use std::path::PathBuf; +use tracing::info; #[cfg_attr(feature = "cli", derive(clap::Args))] #[derive(Debug, Clone)] @@ -81,14 +81,14 @@ where // Attempt to use a user-provided gateway, if possible let user_chosen_gateway_id = common_args.gateway_id; - log::debug!("User chosen gateway id: {user_chosen_gateway_id:?}"); + tracing::debug!("User chosen gateway id: {user_chosen_gateway_id:?}"); let selection_spec = GatewaySelectionSpecification::new( user_chosen_gateway_id.map(|id| id.to_base58_string()), Some(common_args.latency_based_selection), common_args.force_tls_gateway, ); - log::debug!("Gateway selection specification: {selection_spec:?}"); + tracing::debug!("Gateway selection specification: {selection_spec:?}"); let registered_gateways = get_all_registered_identities(&details_store).await?; diff --git a/common/client-core/src/cli_helpers/client_init.rs b/common/client-core/src/cli_helpers/client_init.rs index 03f41ed077..a59d73e261 100644 --- a/common/client-core/src/cli_helpers/client_init.rs +++ b/common/client-core/src/cli_helpers/client_init.rs @@ -12,7 +12,6 @@ use crate::{ }, init::types::{GatewaySelectionSpecification, GatewaySetup, InitResults}, }; -use log::info; use nym_client_core_gateways_storage::GatewayDetails; use nym_crypto::asymmetric::ed25519; use nym_sphinx::addressing::Recipient; @@ -20,6 +19,7 @@ use nym_topology::NymTopology; use nym_validator_client::UserAgent; use rand::rngs::OsRng; use std::path::PathBuf; +use tracing::info; // we can suppress this warning (as suggested by linter itself) since we're only using it in our own code #[allow(async_fn_in_trait)] @@ -130,23 +130,23 @@ where // Attempt to use a user-provided gateway, if possible let user_chosen_gateway_id = common_args.gateway; - log::debug!("User chosen gateway id: {user_chosen_gateway_id:?}"); + tracing::debug!("User chosen gateway id: {user_chosen_gateway_id:?}"); let selection_spec = GatewaySelectionSpecification::new( user_chosen_gateway_id.map(|id| id.to_base58_string()), Some(common_args.latency_based_selection), common_args.force_tls_gateway, ); - log::debug!("Gateway selection specification: {selection_spec:?}"); + tracing::debug!("Gateway selection specification: {selection_spec:?}"); // Load and potentially override config - log::debug!("Init arguments: {init_args:#?}"); + tracing::debug!("Init arguments: {init_args:#?}"); let config = C::construct_config(&init_args); - log::debug!("Constructed config: {config:#?}"); + tracing::debug!("Constructed config: {config:#?}"); let paths = config.common_paths(); let core = config.core_config(); - log::info!( + tracing::info!( "Using nym-api: {}", core.client .nym_api_urls diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 45989eab67..2331b5de68 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -18,6 +18,7 @@ use crate::client::received_buffer::{ ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController, }; use crate::client::replies::reply_controller; +use crate::client::replies::reply_controller::key_rotation_helpers::KeyRotationConfig; use crate::client::replies::reply_controller::{ReplyControllerReceiver, ReplyControllerSender}; use crate::client::replies::reply_storage::{ CombinedReplyStorage, PersistentReplyStorage, ReplyStorageBackend, SentReplyKeys, @@ -34,7 +35,6 @@ use crate::init::{ }; use crate::{config, spawn_future}; use futures::channel::mpsc; -use log::*; use nym_bandwidth_controller::BandwidthController; use nym_client_core_config_types::{ForgetMe, RememberMe}; use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore}; @@ -56,13 +56,18 @@ use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, use nym_task::{TaskClient, TaskHandle}; use nym_topology::provider_trait::TopologyProvider; use nym_topology::HardcodedTopologyProvider; -use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, UserAgent}; +use nym_validator_client::nym_api::NymApiClientExt; +use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, NymApiClient, UserAgent}; +use rand::prelude::SliceRandom; use rand::rngs::OsRng; +use rand::thread_rng; use std::fmt::Debug; use std::os::raw::c_int as RawFd; use std::path::Path; use std::sync::Arc; +use time::OffsetDateTime; use tokio::sync::mpsc::Sender; +use tracing::*; use url::Url; #[cfg(all( @@ -338,6 +343,7 @@ where #[allow(clippy::too_many_arguments)] fn start_real_traffic_controller( controller_config: real_messages_control::Config, + key_rotation_config: KeyRotationConfig, topology_accessor: TopologyAccessor, ack_receiver: AcknowledgementReceiver, input_receiver: InputMessageReceiver, @@ -355,6 +361,7 @@ where RealMessagesController::new( controller_config, + key_rotation_config, ack_receiver, input_receiver, mix_sender, @@ -453,7 +460,7 @@ where }; let gateway_failure = |err| { - log::error!("Could not authenticate and start up the gateway connection - {err}"); + tracing::error!("Could not authenticate and start up the gateway connection - {err}"); ClientCoreError::GatewayClientError { gateway_id: details.gateway_id.to_base58_string(), source: Box::new(err), @@ -555,14 +562,14 @@ where custom_provider: Option>, config_topology: config::Topology, nym_api_urls: Vec, - user_agent: Option, + nym_api_client: NymApiClient, ) -> Box { // if no custom provider was ... provided ..., create one using nym-api custom_provider.unwrap_or_else(|| { Box::new(NymApiTopologyProvider::new( config_topology, nym_api_urls, - user_agent, + nym_api_client, )) }) } @@ -598,7 +605,7 @@ where topology_refresher.try_refresh().await; if let Err(err) = topology_refresher.ensure_topology_is_routable().await { - log::error!( + tracing::error!( "The current network topology seem to be insufficient to route any packets through \ - check if enough nodes and a gateway are online - source: {err}" ); @@ -674,16 +681,26 @@ where // TODO: rename it as it implies the data is persistent whilst one can use InMemBackend async fn setup_persistent_reply_storage( backend: S::ReplyStore, + key_rotation_config: KeyRotationConfig, shutdown: TaskClient, ) -> Result where ::StorageError: Sync + Send, S::ReplyStore: Send + Sync, { - log::trace!("Setup persistent reply storage"); + tracing::trace!("Setup persistent reply storage"); + let now = OffsetDateTime::now_utc(); + let expected_current_key_rotation_start = + key_rotation_config.expected_current_key_rotation_start(now); + // time of the start of one epoch BEFORE the CURRENT rotation has begun + // this indicates the starting time of when packets with the current keys might have been constructed + // (i.e. any surbs OLDER than that MUST BE invalid) + let prior_epoch_start = + expected_current_key_rotation_start - key_rotation_config.epoch_duration; + let persistent_storage = PersistentReplyStorage::new(backend); let mem_store = persistent_storage - .load_state_from_backend() + .load_state_from_backend(prior_epoch_start) .await .map_err(|err| ClientCoreError::SurbStorageError { source: Box::new(err), @@ -725,6 +742,23 @@ where setup_gateway(setup_method, key_store, details_store).await } + fn construct_nym_api_client(config: &Config, user_agent: Option) -> NymApiClient { + let mut nym_api_urls = config.get_nym_api_endpoints(); + nym_api_urls.shuffle(&mut thread_rng()); + + if let Some(user_agent) = user_agent { + NymApiClient::new_with_user_agent(nym_api_urls[0].clone(), user_agent) + } else { + NymApiClient::new(nym_api_urls[0].clone()) + } + } + + async fn determine_key_rotation_state( + client: &NymApiClient, + ) -> Result { + Ok(client.nym_api.get_key_rotation_info().await?.into()) + } + pub async fn start_base(mut self) -> Result where S::ReplyStore: Send + Sync, @@ -789,11 +823,14 @@ where .dkg_query_client .map(|client| BandwidthController::new(credential_store, client)); + let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone()); + let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?; + let topology_provider = Self::setup_topology_provider( self.custom_topology_provider.take(), self.config.debug.topology, self.config.get_nym_api_endpoints(), - self.user_agent.clone(), + nym_api_client, ); let stats_reporter = Self::start_statistics_control( @@ -838,6 +875,7 @@ where let reply_storage = Self::setup_persistent_reply_storage( reply_storage_backend, + key_rotation_config, shutdown.fork("persistent_reply_storage"), ) .await?; @@ -878,6 +916,7 @@ where Self::start_real_traffic_controller( controller_config, + key_rotation_config, shared_topology_accessor.clone(), ack_receiver, input_receiver, diff --git a/common/client-core/src/client/base_client/non_wasm_helpers.rs b/common/client-core/src/client/base_client/non_wasm_helpers.rs index e7ef621c9d..15ad0792c8 100644 --- a/common/client-core/src/client/base_client/non_wasm_helpers.rs +++ b/common/client-core/src/client/base_client/non_wasm_helpers.rs @@ -7,7 +7,6 @@ use crate::client::replies::reply_storage::{ use crate::config; use crate::config::Config; use crate::error::ClientCoreError; -use log::{error, info, trace}; use nym_bandwidth_controller::BandwidthController; use nym_client_core_gateways_storage::OnDiskGatewaysDetails; use nym_credential_storage::storage::Storage as CredentialStorage; @@ -16,6 +15,7 @@ use nym_validator_client::QueryHttpRpcNyxdClient; use std::path::Path; use std::{fs, io}; use time::OffsetDateTime; +use tracing::{error, info, trace}; use url::Url; async fn setup_fresh_backend>( @@ -88,7 +88,7 @@ pub async fn setup_fs_reply_surb_backend>( let db_path = db_path.as_ref(); if db_path.exists() { info!("loading existing surb database"); - match fs_backend::Backend::try_load(db_path, surb_config.fresh_sender_tags).await { + match fs_backend::Backend::try_load(db_path).await { Ok(backend) => Ok(backend), Err(err) => { error!("failed to setup persistent storage backend for our reply needs: {err}. We're going to create a fresh database instead. This behaviour might change in the future"); diff --git a/common/client-core/src/client/cover_traffic_stream.rs b/common/client-core/src/client/cover_traffic_stream.rs index 99b2894d85..a5c08cca0c 100644 --- a/common/client-core/src/client/cover_traffic_stream.rs +++ b/common/client-core/src/client/cover_traffic_stream.rs @@ -6,7 +6,6 @@ use crate::client::topology_control::TopologyAccessor; use crate::{config, spawn_future}; use futures::task::{Context, Poll}; use futures::{Future, Stream, StreamExt}; -use log::*; use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::cover::generate_loop_cover_packet; @@ -19,6 +18,7 @@ use std::pin::Pin; use std::sync::Arc; use std::time::Duration; use tokio::sync::mpsc::error::TrySendError; +use tracing::*; #[cfg(not(target_arch = "wasm32"))] use tokio::time::{sleep, Sleep}; @@ -210,10 +210,10 @@ impl LoopCoverTrafficStream { TrySendError::Full(_) => { // This isn't a problem, if the channel is full means we're already sending the // max amount of messages downstream can handle. - log::debug!("Failed to send cover message - channel full"); + tracing::debug!("Failed to send cover message - channel full"); } TrySendError::Closed(_) => { - log::warn!("Failed to send cover message - channel closed"); + tracing::warn!("Failed to send cover message - channel closed"); } } } else { @@ -258,20 +258,20 @@ impl LoopCoverTrafficStream { tokio::select! { biased; _ = shutdown.recv() => { - log::trace!("LoopCoverTrafficStream: Received shutdown"); + tracing::trace!("LoopCoverTrafficStream: Received shutdown"); } next = self.next() => { if next.is_some() { self.on_new_message().await; } else { - log::trace!("LoopCoverTrafficStream: Stopping since channel closed"); + tracing::trace!("LoopCoverTrafficStream: Stopping since channel closed"); break; } } } } shutdown.recv_timeout().await; - log::debug!("LoopCoverTrafficStream: Exiting"); + tracing::debug!("LoopCoverTrafficStream: Exiting"); }) } } diff --git a/common/client-core/src/client/inbound_messages.rs b/common/client-core/src/client/inbound_messages.rs index ad5e4b0196..b74c9195be 100644 --- a/common/client-core/src/client/inbound_messages.rs +++ b/common/client-core/src/client/inbound_messages.rs @@ -135,7 +135,9 @@ impl InputMessage { recipient_tag, data, lane, - max_retransmissions: None, + // \/ set it to SOME sane default so that if we run out of surbs and constantly + // fail to request more, we wouldn't be stuck in limbo + max_retransmissions: Some(10), }; if let Some(packet_type) = packet_type { InputMessage::new_wrapper(message, packet_type) diff --git a/common/client-core/src/client/mix_traffic/mod.rs b/common/client-core/src/client/mix_traffic/mod.rs index 1114de9d89..6f58c477df 100644 --- a/common/client-core/src/client/mix_traffic/mod.rs +++ b/common/client-core/src/client/mix_traffic/mod.rs @@ -4,10 +4,10 @@ use crate::client::mix_traffic::transceiver::GatewayTransceiver; use crate::error::ClientCoreError; use crate::spawn_future; -use log::*; use nym_gateway_requests::ClientRequest; use nym_sphinx::forwarding::packet::MixPacket; use nym_task::TaskClient; +use tracing::*; use transceiver::ErasedGatewayError; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender>; @@ -138,7 +138,7 @@ impl MixTrafficController { } }, None => { - log::trace!("MixTrafficController: Stopping since channel closed"); + tracing::trace!("MixTrafficController: Stopping since channel closed"); break; } }, @@ -150,18 +150,18 @@ impl MixTrafficController { }; }, None => { - log::trace!("MixTrafficController, client request channel closed"); + tracing::trace!("MixTrafficController, client request channel closed"); } }, _ = self.task_client.recv() => { - log::trace!("MixTrafficController: Received shutdown"); + tracing::trace!("MixTrafficController: Received shutdown"); break; } } } self.task_client.recv_timeout().await; - log::debug!("MixTrafficController: Exiting"); + tracing::debug!("MixTrafficController: Exiting"); }); } } diff --git a/common/client-core/src/client/mix_traffic/transceiver.rs b/common/client-core/src/client/mix_traffic/transceiver.rs index 6ec0e68ae6..3bcc209e05 100644 --- a/common/client-core/src/client/mix_traffic/transceiver.rs +++ b/common/client-core/src/client/mix_traffic/transceiver.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use log::{debug, error}; use nym_credential_storage::storage::Storage as CredentialStorage; use nym_crypto::asymmetric::ed25519; use nym_gateway_client::error::GatewayClientError; @@ -14,6 +13,7 @@ use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use std::fmt::Debug; use std::os::raw::c_int as RawFd; use thiserror::Error; +use tracing::{debug, error}; #[cfg(not(target_arch = "wasm32"))] use futures::channel::oneshot; @@ -27,7 +27,7 @@ fn erase_err(err: E) -> ErasedGate ErasedGatewayError(Box::new(err)) } -/// This combines combines the functionalities of being able to send and receive mix packets. +/// This combines the functionalities of being able to send and receive mix packets. #[async_trait] pub trait GatewayTransceiver: GatewaySender + GatewayReceiver { fn gateway_identity(&self) -> ed25519::PublicKey; @@ -87,7 +87,7 @@ impl GatewayTransceiver for Box { message: ClientRequest, ) -> Result<(), GatewayClientError> { let _ = (**self).send_client_request(message.clone()).await?; - log::debug!("Sent client request: {:?}", message); + tracing::debug!("Sent client request: {:?}", message); Ok(()) } } diff --git a/common/client-core/src/client/real_messages_control/acknowledgement_control/acknowledgement_listener.rs b/common/client-core/src/client/real_messages_control/acknowledgement_control/acknowledgement_listener.rs index 4b5d483ee5..b183fe4c05 100644 --- a/common/client-core/src/client/real_messages_control/acknowledgement_control/acknowledgement_listener.rs +++ b/common/client-core/src/client/real_messages_control/acknowledgement_control/acknowledgement_listener.rs @@ -5,7 +5,6 @@ use super::action_controller::{AckActionSender, Action}; use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender}; use futures::StreamExt; -use log::*; use nym_gateway_client::AcknowledgementReceiver; use nym_sphinx::{ acknowledgements::{identifier::recover_identifier, AckKey}, @@ -13,6 +12,7 @@ use nym_sphinx::{ }; use nym_task::TaskClient; use std::sync::Arc; +use tracing::*; /// Module responsible for listening for any data resembling acknowledgements from the network /// and firing actions to remove them from the 'Pending' state. @@ -93,16 +93,16 @@ impl AcknowledgementListener { acks = self.ack_receiver.next() => match acks { Some(acks) => self.handle_ack_receiver_item(acks).await, None => { - log::trace!("AcknowledgementListener: Stopping since channel closed"); + tracing::trace!("AcknowledgementListener: Stopping since channel closed"); break; } }, _ = self.task_client.recv() => { - log::trace!("AcknowledgementListener: Received shutdown"); + tracing::trace!("AcknowledgementListener: Received shutdown"); } } } self.task_client.recv_timeout().await; - log::debug!("AcknowledgementListener: Exiting"); + tracing::debug!("AcknowledgementListener: Exiting"); } } diff --git a/common/client-core/src/client/real_messages_control/acknowledgement_control/action_controller.rs b/common/client-core/src/client/real_messages_control/acknowledgement_control/action_controller.rs index 6235b7c477..d335196983 100644 --- a/common/client-core/src/client/real_messages_control/acknowledgement_control/action_controller.rs +++ b/common/client-core/src/client/real_messages_control/acknowledgement_control/action_controller.rs @@ -5,7 +5,6 @@ use super::PendingAcknowledgement; use crate::client::real_messages_control::acknowledgement_control::RetransmissionRequestSender; use futures::channel::mpsc; use futures::StreamExt; -use log::*; use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey}; use nym_sphinx::chunking::fragment::FragmentIdentifier; use nym_sphinx::Delay as SphinxDelay; @@ -13,6 +12,7 @@ use nym_task::TaskClient; use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; +use tracing::*; pub(crate) type AckActionSender = mpsc::UnboundedSender; pub(crate) type AckActionReceiver = mpsc::UnboundedReceiver; @@ -241,7 +241,7 @@ impl ActionController { .unbounded_send(Arc::downgrade(pending_ack_data)) { if !self.task_client.is_shutdown_poll() { - log::error!("Failed to send pending ack for retransmission: {err}"); + tracing::error!("Failed to send pending ack for retransmission: {err}"); } } } else { @@ -269,7 +269,7 @@ impl ActionController { action = self.incoming_actions.next() => match action { Some(action) => self.process_action(action), None => { - log::trace!( + tracing::trace!( "ActionController: Stopping since incoming actions channel closed" ); break; @@ -278,17 +278,17 @@ impl ActionController { expired_ack = self.pending_acks_timers.next() => match expired_ack { Some(expired_ack) => self.handle_expired_ack_timer(expired_ack), None => { - log::trace!("ActionController: Stopping since ack channel closed"); + tracing::trace!("ActionController: Stopping since ack channel closed"); break; } }, _ = self.task_client.recv() => { - log::trace!("ActionController: Received shutdown"); + tracing::trace!("ActionController: Received shutdown"); break; } } } self.task_client.recv_timeout().await; - log::debug!("ActionController: Exiting"); + tracing::debug!("ActionController: Exiting"); } } diff --git a/common/client-core/src/client/real_messages_control/acknowledgement_control/input_message_listener.rs b/common/client-core/src/client/real_messages_control/acknowledgement_control/input_message_listener.rs index 49da4bce9c..9414b3a2fe 100644 --- a/common/client-core/src/client/real_messages_control/acknowledgement_control/input_message_listener.rs +++ b/common/client-core/src/client/real_messages_control/acknowledgement_control/input_message_listener.rs @@ -5,7 +5,6 @@ use crate::client::inbound_messages::{InputMessage, InputMessageReceiver}; use crate::client::real_messages_control::message_handler::MessageHandler; use crate::client::real_messages_control::real_traffic_stream::RealMessage; use crate::client::replies::reply_controller::ReplyControllerSender; -use log::*; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::forwarding::packet::MixPacket; @@ -13,6 +12,7 @@ use nym_sphinx::params::PacketType; use nym_task::connections::TransmissionLane; use nym_task::TaskClient; use rand::{CryptoRng, Rng}; +use tracing::*; /// Module responsible for dealing with the received messages: splitting them, creating acknowledgements, /// putting everything into sphinx packets, etc. @@ -228,16 +228,16 @@ where self.on_input_message(input_msg).await; }, None => { - log::trace!("InputMessageListener: Stopping since channel closed"); + tracing::trace!("InputMessageListener: Stopping since channel closed"); break; } }, _ = self.task_client.recv() => { - log::trace!("InputMessageListener: Received shutdown"); + tracing::trace!("InputMessageListener: Received shutdown"); } } } self.task_client.recv_timeout().await; - log::debug!("InputMessageListener: Exiting"); + tracing::debug!("InputMessageListener: Exiting"); } } diff --git a/common/client-core/src/client/real_messages_control/acknowledgement_control/mod.rs b/common/client-core/src/client/real_messages_control/acknowledgement_control/mod.rs index c3f63b07ad..2cad9ef2c2 100644 --- a/common/client-core/src/client/real_messages_control/acknowledgement_control/mod.rs +++ b/common/client-core/src/client/real_messages_control/acknowledgement_control/mod.rs @@ -13,7 +13,6 @@ use crate::client::replies::reply_controller::ReplyControllerSender; use crate::spawn_future; use action_controller::AckActionReceiver; use futures::channel::mpsc; -use log::*; use nym_gateway_client::AcknowledgementReceiver; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::params::{PacketSize, PacketType}; @@ -30,6 +29,7 @@ use std::{ sync::{Arc, Weak}, time::Duration, }; +use tracing::*; pub(crate) use action_controller::{AckActionSender, Action}; diff --git a/common/client-core/src/client/real_messages_control/acknowledgement_control/retransmission_request_listener.rs b/common/client-core/src/client/real_messages_control/acknowledgement_control/retransmission_request_listener.rs index 2f7b5d976e..aab826de7a 100644 --- a/common/client-core/src/client/real_messages_control/acknowledgement_control/retransmission_request_listener.rs +++ b/common/client-core/src/client/real_messages_control/acknowledgement_control/retransmission_request_listener.rs @@ -10,13 +10,13 @@ use crate::client::real_messages_control::message_handler::{MessageHandler, Prep use crate::client::real_messages_control::real_traffic_stream::RealMessage; use crate::client::replies::reply_controller::ReplyControllerSender; use futures::StreamExt; -use log::*; use nym_sphinx::chunking::fragment::Fragment; use nym_sphinx::preparer::PreparedFragment; use nym_sphinx::{addressing::clients::Recipient, params::PacketType}; use nym_task::{connections::TransmissionLane, TaskClient}; use rand::{CryptoRng, Rng}; use std::sync::{Arc, Weak}; +use tracing::*; // responsible for packet retransmission upon fired timer pub(super) struct RetransmissionRequestListener { @@ -182,16 +182,16 @@ where timed_out_ack = self.request_receiver.next() => match timed_out_ack { Some(timed_out_ack) => self.on_retransmission_request(timed_out_ack, packet_type).await, None => { - log::trace!("RetransmissionRequestListener: Stopping since channel closed"); + tracing::trace!("RetransmissionRequestListener: Stopping since channel closed"); break; } }, _ = self.task_client.recv() => { - log::trace!("RetransmissionRequestListener: Received shutdown"); + tracing::trace!("RetransmissionRequestListener: Received shutdown"); } } } self.task_client.recv_timeout().await; - log::debug!("RetransmissionRequestListener: Exiting"); + tracing::debug!("RetransmissionRequestListener: Exiting"); } } diff --git a/common/client-core/src/client/real_messages_control/acknowledgement_control/sent_notification_listener.rs b/common/client-core/src/client/real_messages_control/acknowledgement_control/sent_notification_listener.rs index 0b563c375d..9ee9cdf461 100644 --- a/common/client-core/src/client/real_messages_control/acknowledgement_control/sent_notification_listener.rs +++ b/common/client-core/src/client/real_messages_control/acknowledgement_control/sent_notification_listener.rs @@ -4,9 +4,9 @@ use super::action_controller::{AckActionSender, Action}; use super::SentPacketNotificationReceiver; use futures::StreamExt; -use log::*; use nym_sphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID}; use nym_task::TaskClient; +use tracing::*; /// Module responsible for starting up retransmission timers. /// It is required because when we send our packet to the `real traffic stream` controlled @@ -56,17 +56,17 @@ impl SentNotificationListener { self.on_sent_message(frag_id).await; } None => { - log::trace!("SentNotificationListener: Stopping since channel closed"); + tracing::trace!("SentNotificationListener: Stopping since channel closed"); break; } }, _ = self.task_client.recv() => { - log::trace!("SentNotificationListener: Received shutdown"); + tracing::trace!("SentNotificationListener: Received shutdown"); break; } } } assert!(self.task_client.is_shutdown_poll()); - log::debug!("SentNotificationListener: Exiting"); + tracing::debug!("SentNotificationListener: Exiting"); } } diff --git a/common/client-core/src/client/real_messages_control/message_handler.rs b/common/client-core/src/client/real_messages_control/message_handler.rs index c88a6257ce..0f4d902f54 100644 --- a/common/client-core/src/client/real_messages_control/message_handler.rs +++ b/common/client-core/src/client/real_messages_control/message_handler.rs @@ -9,6 +9,7 @@ use crate::client::real_messages_control::{AckActionSender, Action}; use crate::client::replies::reply_controller::MaxRetransmissions; use crate::client::replies::reply_storage::{ReceivedReplySurbsMap, SentReplyKeys, UsedSenderTags}; use crate::client::topology_control::{TopologyAccessor, TopologyReadPermit}; +use nym_client_core_surb_storage::RetrievedReplySurb; use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, RepliableMessage, ReplyMessage}; @@ -44,10 +45,7 @@ pub enum PreparationError { } impl PreparationError { - fn return_surbs( - self, - returned_surbs: Vec, - ) -> SurbWrappedPreparationError { + fn return_surbs(self, returned_surbs: Vec) -> SurbWrappedPreparationError { SurbWrappedPreparationError { source: self, returned_surbs: Some(returned_surbs), @@ -61,7 +59,7 @@ pub struct SurbWrappedPreparationError { #[source] source: PreparationError, - returned_surbs: Option>, + returned_surbs: Option>, } impl From for SurbWrappedPreparationError @@ -83,7 +81,7 @@ impl SurbWrappedPreparationError { target: &AnonymousSenderTag, ) -> PreparationError { if let Some(reply_surbs) = self.returned_surbs { - surb_storage.insert_surbs(target, reply_surbs) + surb_storage.re_insert_reply_surbs(target, reply_surbs) } self.source } @@ -224,6 +222,10 @@ where } } + pub(crate) fn topology_access_handle(&self) -> &TopologyAccessor { + &self.topology_access + } + fn get_or_create_sender_tag(&mut self, recipient: &Recipient) -> AnonymousSenderTag { if let Some(existing) = self.tag_storage.try_get_existing(recipient) { trace!("we already had sender tag for {recipient}"); @@ -291,7 +293,7 @@ where &mut self, target: AnonymousSenderTag, message: ReplyMessage, - reply_surb: ReplySurbWithKeyRotation, + reply_surb: RetrievedReplySurb, is_extra_surb_request: bool, ) -> Result<(), SurbWrappedPreparationError> { let msg = NymMessage::new_reply(message); @@ -322,7 +324,10 @@ where Some(chunk.fragment_identifier()), ); let delay = prepared_fragment.total_delay; - let max_retransmissions = None; + + // we have to set a maximum number of retransmissions in case we fail to retrieve + // surbs for a long period of time; we don't want to be stuck constantly resending the data + let max_retransmissions = Some(10); let pending_ack = PendingAcknowledgement::new_anonymous( chunk, delay, @@ -345,7 +350,7 @@ where pub(crate) async fn try_request_additional_reply_surbs( &mut self, from: AnonymousSenderTag, - reply_surb: ReplySurbWithKeyRotation, + reply_surb: RetrievedReplySurb, amount: u32, ) -> Result<(), SurbWrappedPreparationError> { debug!("requesting {amount} reply SURBs from {from}"); @@ -385,11 +390,9 @@ where &mut self, target: AnonymousSenderTag, fragments: Vec, - reply_surbs: Vec, + reply_surbs: impl IntoIterator, lane: TransmissionLane, ) -> Result<(), SurbWrappedPreparationError> { - // TODO: technically this is performing an unnecessary cloning, but in the grand scheme of things - // is it really that bad? self.try_send_reply_chunks( target, fragments.into_iter().map(|f| (lane, f)).collect(), @@ -402,7 +405,7 @@ where &mut self, target: AnonymousSenderTag, fragments: Vec<(TransmissionLane, FragmentWithMaxRetransmissions)>, - reply_surbs: Vec, + reply_surbs: impl IntoIterator, ) -> Result<(), SurbWrappedPreparationError> { let prepared_fragments = self .prepare_reply_chunks_for_sending( @@ -564,7 +567,7 @@ where ) .await?; - log::trace!("storing {} reply keys", reply_keys.len()); + tracing::trace!("storing {} reply keys", reply_keys.len()); self.reply_key_storage.insert_multiple(reply_keys); Ok(()) @@ -604,7 +607,7 @@ where ) .await?; - log::trace!("storing {} reply keys", reply_keys.len()); + tracing::trace!("storing {} reply keys", reply_keys.len()); self.reply_key_storage.insert_multiple(reply_keys); Ok(()) @@ -634,20 +637,12 @@ where pub(crate) async fn prepare_reply_chunks_for_sending( &mut self, fragments: Vec, - reply_surbs: Vec, + reply_surbs: impl IntoIterator, ) -> Result, SurbWrappedPreparationError> { - debug_assert_eq!( - fragments.len(), - reply_surbs.len(), - "attempted to send {} fragments with {} reply surbs", - fragments.len(), - reply_surbs.len() - ); - let topology_permit = self.topology_access.get_read_permit().await; let topology = match self.get_topology(&topology_permit) { Ok(topology) => topology, - Err(err) => return Err(err.return_surbs(reply_surbs)), + Err(err) => return Err(err.return_surbs(reply_surbs.into_iter().collect())), }; Ok(fragments @@ -660,7 +655,7 @@ where fragment, topology, &self.config.ack_key, - reply_surb, + reply_surb.into(), PacketType::Mix, ) .unwrap() @@ -670,7 +665,7 @@ where pub(crate) async fn try_prepare_single_reply_chunk_for_sending( &mut self, - reply_surb: ReplySurbWithKeyRotation, + reply_surb: RetrievedReplySurb, chunk: Fragment, ) -> Result { let topology_permit = self.topology_access.get_read_permit().await; @@ -683,7 +678,7 @@ where chunk, topology, &self.config.ack_key, - reply_surb, + reply_surb.into(), PacketType::Mix, )?; diff --git a/common/client-core/src/client/real_messages_control/mod.rs b/common/client-core/src/client/real_messages_control/mod.rs index 6c0715cd0d..a30b28fc41 100644 --- a/common/client-core/src/client/real_messages_control/mod.rs +++ b/common/client-core/src/client/real_messages_control/mod.rs @@ -24,7 +24,6 @@ use crate::{ spawn_future, }; use futures::channel::mpsc; -use log::*; use nym_gateway_client::AcknowledgementReceiver; use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::addressing::clients::Recipient; @@ -34,7 +33,9 @@ use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths}; use nym_task::TaskClient; use rand::{rngs::OsRng, CryptoRng, Rng}; use std::sync::Arc; +use tracing::*; +use crate::client::replies::reply_controller::key_rotation_helpers::KeyRotationConfig; pub(crate) use acknowledgement_control::{AckActionSender, Action}; pub(crate) mod acknowledgement_control; @@ -85,12 +86,6 @@ impl<'a> From<&'a Config> for real_traffic_stream::Config { } } -impl<'a> From<&'a Config> for reply_controller::Config { - fn from(cfg: &'a Config) -> Self { - reply_controller::Config::new(cfg.reply_surbs) - } -} - impl<'a> From<&'a Config> for message_handler::Config { fn from(cfg: &'a Config) -> Self { message_handler::Config::new( @@ -139,6 +134,7 @@ impl RealMessagesController { #[allow(clippy::too_many_arguments)] pub(crate) fn new( config: Config, + key_rotation_config: KeyRotationConfig, ack_receiver: AcknowledgementReceiver, input_receiver: InputMessageReceiver, mix_sender: BatchMixMessageSender, @@ -169,7 +165,8 @@ impl RealMessagesController { // create all configs for the components let ack_control_config = (&config).into(); let out_queue_config = (&config).into(); - let reply_controller_config = (&config).into(); + let reply_controller_config = + reply_controller::Config::new(config.reply_surbs, key_rotation_config); let message_handler_config = (&config).into(); // create the actual components diff --git a/common/client-core/src/client/real_messages_control/real_traffic_stream.rs b/common/client-core/src/client/real_messages_control/real_traffic_stream.rs index edc313990f..ebc6b40380 100644 --- a/common/client-core/src/client/real_messages_control/real_traffic_stream.rs +++ b/common/client-core/src/client/real_messages_control/real_traffic_stream.rs @@ -9,7 +9,6 @@ use crate::client::transmission_buffer::TransmissionBuffer; use crate::config; use futures::task::{Context, Poll}; use futures::{Future, Stream, StreamExt}; -use log::*; use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::chunking::fragment::FragmentIdentifier; @@ -27,6 +26,7 @@ use rand::{CryptoRng, Rng}; use std::pin::Pin; use std::sync::Arc; use std::time::Duration; +use tracing::*; #[cfg(not(target_arch = "wasm32"))] use tokio::time::{sleep, Sleep}; @@ -280,7 +280,7 @@ where if let Err(err) = self.mix_tx.send(vec![next_message]).await { if !self.task_client.is_shutdown_poll() { - log::error!("Failed to send: {err}"); + tracing::error!("Failed to send: {err}"); } } else { let event = if fragment_id.is_some() { @@ -313,7 +313,7 @@ where } fn on_close_connection(&mut self, connection_id: ConnectionId) { - log::debug!("Removing lane for connection: {connection_id}"); + tracing::debug!("Removing lane for connection: {connection_id}"); self.transmission_buffer .remove(&TransmissionLane::ConnectionId(connection_id)); } @@ -325,7 +325,7 @@ where fn adjust_current_average_message_sending_delay(&mut self) { let used_slots = self.mix_tx.max_capacity() - self.mix_tx.capacity(); - log::trace!( + tracing::trace!( "used_slots: {used_slots}, current_multiplier: {}", self.sending_delay_controller.current_multiplier() ); @@ -334,7 +334,7 @@ where .sending_delay_controller .is_backpressure_currently_detected(used_slots) { - log::trace!("Backpressure detected"); + tracing::trace!("Backpressure detected"); self.sending_delay_controller.record_backpressure_detected(); } @@ -436,7 +436,7 @@ where Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some((real_messages, conn_id))) => { - log::trace!("handling real_messages: size: {}", real_messages.len()); + tracing::trace!("handling real_messages: size: {}", real_messages.len()); self.transmission_buffer.store(&conn_id, real_messages); let real_next = self.pop_next_message().expect("Just stored one"); @@ -483,7 +483,7 @@ where Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some((real_messages, conn_id))) => { - log::trace!("handling real_messages: size: {}", real_messages.len()); + tracing::trace!("handling real_messages: size: {}", real_messages.len()); // First store what we got for the given connection id self.transmission_buffer.store(&conn_id, real_messages); @@ -538,11 +538,11 @@ where }; if packets > 1000 { - log::warn!("{status_str}"); + tracing::warn!("{status_str}"); } else if packets > 0 { - log::info!("{status_str}"); + tracing::info!("{status_str}"); } else { - log::debug!("{status_str}"); + tracing::debug!("{status_str}"); } // Send status message to whoever is listening (possibly UI) @@ -566,7 +566,7 @@ where tokio::select! { biased; _ = shutdown.recv() => { - log::trace!("OutQueueControl: Received shutdown"); + tracing::trace!("OutQueueControl: Received shutdown"); break; } _ = status_timer.tick() => { @@ -575,7 +575,7 @@ where next_message = self.next() => if let Some(next_message) = next_message { self.on_message(next_message).await; } else { - log::trace!("OutQueueControl: Stopping since channel closed"); + tracing::trace!("OutQueueControl: Stopping since channel closed"); break; } } @@ -589,18 +589,18 @@ where tokio::select! { biased; _ = shutdown.recv() => { - log::trace!("OutQueueControl: Received shutdown"); + tracing::trace!("OutQueueControl: Received shutdown"); } next_message = self.next() => if let Some(next_message) = next_message { self.on_message(next_message).await; } else { - log::trace!("OutQueueControl: Stopping since channel closed"); + tracing::trace!("OutQueueControl: Stopping since channel closed"); break; } } } } - log::debug!("OutQueueControl: Exiting"); + tracing::debug!("OutQueueControl: Exiting"); } } diff --git a/common/client-core/src/client/real_messages_control/real_traffic_stream/sending_delay_controller.rs b/common/client-core/src/client/real_messages_control/real_traffic_stream/sending_delay_controller.rs index d038ac624e..faf740d4b0 100644 --- a/common/client-core/src/client/real_messages_control/real_traffic_stream/sending_delay_controller.rs +++ b/common/client-core/src/client/real_messages_control/real_traffic_stream/sending_delay_controller.rs @@ -98,12 +98,12 @@ impl SendingDelayController { self.current_multiplier = (self.current_multiplier + 1).clamp(self.lower_bound, self.upper_bound); self.time_when_changed = get_time_now(); - log::debug!( + tracing::debug!( "Increasing sending delay multiplier to: {}", self.current_multiplier ); } else { - log::warn!("Trying to increase delay multipler higher than allowed"); + tracing::warn!("Trying to increase delay multipler higher than allowed"); } } @@ -112,7 +112,7 @@ impl SendingDelayController { self.current_multiplier = (self.current_multiplier - 1).clamp(self.lower_bound, self.upper_bound); self.time_when_changed = get_time_now(); - log::debug!( + tracing::debug!( "Decreasing sending delay multiplier to: {}", self.current_multiplier ); @@ -164,11 +164,11 @@ impl SendingDelayController { self.current_multiplier() ); if self.current_multiplier() > 0 { - log::debug!("{}", status_str); + tracing::debug!("{}", status_str); } else if self.current_multiplier() > 1 { - log::info!("{}", status_str); + tracing::info!("{}", status_str); } else if self.current_multiplier() > 2 { - log::warn!("{}", status_str); + tracing::warn!("{}", status_str); } self.time_when_logged_about_elevated_multiplier = now; } diff --git a/common/client-core/src/client/received_buffer.rs b/common/client-core/src/client/received_buffer.rs index 380e519460..044b5ee355 100644 --- a/common/client-core/src/client/received_buffer.rs +++ b/common/client-core/src/client/received_buffer.rs @@ -8,7 +8,6 @@ use crate::spawn_future; use futures::channel::mpsc; use futures::lock::Mutex; use futures::StreamExt; -use log::*; use nym_crypto::asymmetric::x25519; use nym_crypto::Digest; use nym_gateway_client::MixnetMessageReceiver; @@ -24,6 +23,7 @@ use nym_task::TaskClient; use std::collections::HashSet; use std::sync::Arc; use std::time::{Duration, Instant}; +use tracing::*; // The interval at which we check for stale buffers const STALE_BUFFER_CHECK_INTERVAL: Duration = Duration::from_secs(10); @@ -310,13 +310,15 @@ impl ReceivedMessagesBuffer { } }; - if let Err(err) = self.reply_controller_sender.send_additional_surbs( - msg.sender_tag, - reply_surbs, - from_surb_request, - ) { - if !self.task_client.is_shutdown_poll() { - error!("{err}"); + if !reply_surbs.is_empty() { + if let Err(err) = self.reply_controller_sender.send_additional_surbs( + msg.sender_tag, + reply_surbs, + from_surb_request, + ) { + if !self.task_client.is_shutdown_poll() { + error!("{err}"); + } } } } @@ -500,20 +502,20 @@ impl RequestReceiver { tokio::select! { biased; _ = self.task_client.recv() => { - log::trace!("RequestReceiver: Received shutdown"); + tracing::trace!("RequestReceiver: Received shutdown"); } request = self.query_receiver.next() => { if let Some(message) = request { self.handle_message(message).await } else { - log::trace!("RequestReceiver: Stopping since channel closed"); + tracing::trace!("RequestReceiver: Stopping since channel closed"); break; } }, } } self.task_client.recv().await; - log::debug!("RequestReceiver: Exiting"); + tracing::debug!("RequestReceiver: Exiting"); } } @@ -544,17 +546,17 @@ impl FragmentedMessageReceiver { if let Some(new_messages) = new_messages { self.received_buffer.handle_new_received(new_messages).await?; } else { - log::trace!("FragmentedMessageReceiver: Stopping since channel closed"); + tracing::trace!("FragmentedMessageReceiver: Stopping since channel closed"); break; } }, _ = self.task_client.recv_with_delay() => { - log::trace!("FragmentedMessageReceiver: Received shutdown"); + tracing::trace!("FragmentedMessageReceiver: Received shutdown"); } } } self.task_client.recv_timeout().await; - log::debug!("FragmentedMessageReceiver: Exiting"); + tracing::debug!("FragmentedMessageReceiver: Exiting"); Ok(()) } } diff --git a/common/client-core/src/client/replies/reply_controller/key_rotation_helpers.rs b/common/client-core/src/client/replies/reply_controller/key_rotation_helpers.rs new file mode 100644 index 0000000000..21553a4235 --- /dev/null +++ b/common/client-core/src/client/replies/reply_controller/key_rotation_helpers.rs @@ -0,0 +1,169 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_topology::NymTopologyMetadata; +use nym_validator_client::models::{ + EpochId, KeyRotationId, KeyRotationInfoResponse, KeyRotationState, +}; +use std::time::Duration; +use time::OffsetDateTime; + +#[derive(Clone, Copy)] +pub(crate) enum SurbRefreshState { + WaitingForNextRotation { last_known: KeyRotationId }, + ScheduledForNextInvocation, +} + +#[derive(Clone, Copy)] +pub(crate) struct ReferenceEpoch { + pub(crate) absolute_epoch_id: EpochId, + pub(crate) start_time: OffsetDateTime, +} + +#[derive(Clone, Copy)] +pub(crate) struct KeyRotationConfig { + pub(crate) epoch_duration: Duration, + pub(crate) rotation_state: KeyRotationState, + pub(crate) reference_epoch: ReferenceEpoch, +} + +impl From for KeyRotationConfig { + fn from(value: KeyRotationInfoResponse) -> Self { + KeyRotationConfig { + epoch_duration: value.details.epoch_duration, + rotation_state: value.details.key_rotation_state, + reference_epoch: ReferenceEpoch { + absolute_epoch_id: value.details.current_absolute_epoch_id, + start_time: value.details.current_epoch_start, + }, + } + } +} + +impl KeyRotationConfig { + pub(crate) fn rotation_lifetime(&self) -> Duration { + (self.rotation_state.validity_epochs + 1) * self.epoch_duration + } + + pub(crate) fn key_rotation_id(&self, current_absolute_epoch_id: EpochId) -> KeyRotationId { + self.rotation_state + .key_rotation_id(current_absolute_epoch_id) + } + + // this is called with the assumption that now is always > reference epoch start + pub(crate) fn expected_current_epoch_id(&self, now: OffsetDateTime) -> EpochId { + let diff_secs = (now - self.reference_epoch.start_time).as_seconds_f64(); + let epochs = (diff_secs / self.epoch_duration.as_secs_f64()).floor() as u32; + + self.reference_epoch.absolute_epoch_id + epochs + } + + fn initial_rotation_epoch_start(&self) -> OffsetDateTime { + let epochs_diff = self + .reference_epoch + .absolute_epoch_id + .saturating_sub(self.rotation_state.initial_epoch_id); + + self.reference_epoch.start_time - epochs_diff * self.epoch_duration + } + + pub(crate) fn key_rotation_start(&self, key_rotation_id: KeyRotationId) -> OffsetDateTime { + let rotation_duration = self.rotation_state.validity_epochs * self.epoch_duration; + let initial_start = self.initial_rotation_epoch_start(); + + // note: key rotation starts from 0 + initial_start + rotation_duration * key_rotation_id + } + + pub(crate) fn expected_current_key_rotation_id(&self, now: OffsetDateTime) -> KeyRotationId { + let expected_current_epoch = self.expected_current_epoch_id(now); + self.key_rotation_id(expected_current_epoch) + } + + pub(crate) fn expected_current_key_rotation_start( + &self, + now: OffsetDateTime, + ) -> OffsetDateTime { + let expected_current_key_rotation_id = self.expected_current_key_rotation_id(now); + self.key_rotation_start(expected_current_key_rotation_id) + } + + pub(crate) fn epoch_stuck(&self, topology_metadata: NymTopologyMetadata) -> bool { + // add leeway of 2mins each direction since transition is not instantaneous + let lower_bound = topology_metadata.refreshed_at - Duration::from_secs(2); + let upper_bound = topology_metadata.refreshed_at + Duration::from_secs(2); + + let expected_epoch_lower = self.expected_current_epoch_id(lower_bound); + let expected_epoch_upper = self.expected_current_epoch_id(upper_bound); + + topology_metadata.absolute_epoch_id != expected_epoch_lower + && topology_metadata.absolute_epoch_id != expected_epoch_upper + } +} + +#[cfg(test)] +mod tests { + use super::*; + use time::macros::datetime; + + fn mock_config() -> KeyRotationConfig { + KeyRotationConfig { + epoch_duration: Duration::from_secs(60 * 60), + rotation_state: KeyRotationState { + validity_epochs: 10, + initial_epoch_id: 80, + }, + reference_epoch: ReferenceEpoch { + absolute_epoch_id: 100, + start_time: datetime!(2025-06-30 12:00:00+00:00), + }, + } + } + + #[test] + fn expected_current_key_rotation_start() { + // rot0: 80-89 + // rot1: 90-99 + // rot2: 100-109 + // rot3: 110-119 + // ... etc + let cfg = mock_config(); + + assert_eq!( + cfg.initial_rotation_epoch_start(), + datetime!(2025-06-29 16:00:00+00:00) + ); + + let fake_now = datetime!(2025-06-30 12:00:00+00:00); + assert_eq!(cfg.expected_current_epoch_id(fake_now), 100); + assert_eq!(cfg.expected_current_key_rotation_id(fake_now), 2); + assert_eq!( + cfg.expected_current_key_rotation_start(fake_now), + datetime!(2025-06-30 12:00:00+00:00) + ); + + let fake_now = datetime!(2025-06-30 12:30:00+00:00); + assert_eq!(cfg.expected_current_epoch_id(fake_now), 100); + assert_eq!(cfg.expected_current_key_rotation_id(fake_now), 2); + assert_eq!( + cfg.expected_current_key_rotation_start(fake_now), + datetime!(2025-06-30 12:00:00+00:00) + ); + + let fake_now = datetime!(2025-06-30 13:01:00+00:00); + assert_eq!(cfg.expected_current_epoch_id(fake_now), 101); + assert_eq!(cfg.expected_current_key_rotation_id(fake_now), 2); + assert_eq!( + cfg.expected_current_key_rotation_start(fake_now), + datetime!(2025-06-30 12:00:00+00:00) + ); + + let fake_now = datetime!(2025-06-30 22:02:00+00:00); + assert_eq!(cfg.expected_current_epoch_id(fake_now), 110); + assert_eq!(cfg.expected_current_key_rotation_id(fake_now), 3); + assert_eq!( + cfg.expected_current_key_rotation_start(fake_now), + datetime!(2025-06-30 22:00:00+00:00) + ); + } +} diff --git a/common/client-core/src/client/replies/reply_controller/mod.rs b/common/client-core/src/client/replies/reply_controller/mod.rs index c043af1bdf..60004c2011 100644 --- a/common/client-core/src/client/replies/reply_controller/mod.rs +++ b/common/client-core/src/client/replies/reply_controller/mod.rs @@ -1,45 +1,46 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement; -use crate::client::real_messages_control::message_handler::{ - FragmentWithMaxRetransmissions, MessageHandler, PreparationError, -}; +use crate::client::helpers::new_interval_stream; +use crate::client::real_messages_control::message_handler::MessageHandler; +use crate::client::replies::reply_controller::key_rotation_helpers::KeyRotationConfig; use crate::client::replies::reply_storage::CombinedReplyStorage; -use futures::channel::oneshot; +use crate::config; use futures::StreamExt; -use log::{debug, error, info, trace, warn}; -use nym_sphinx::addressing::clients::Recipient; -use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; -use nym_sphinx::anonymous_replies::ReplySurbWithKeyRotation; -use nym_sphinx::chunking::fragment::FragmentIdentifier; -use nym_task::connections::{ConnectionId, TransmissionLane}; use nym_task::TaskClient; +use rand::rngs::OsRng; use rand::{CryptoRng, Rng}; -use std::cmp::{max, min}; -use std::collections::btree_map::Entry; -use std::collections::{BTreeMap, HashMap}; -use std::sync::{Arc, Weak}; use std::time::Duration; use time::OffsetDateTime; +use tracing::debug; -use crate::client::helpers::new_interval_stream; -use crate::client::transmission_buffer::TransmissionBuffer; -use crate::config; +use crate::client::replies::reply_controller::receiver_controller::ReceiverReplyController; +use crate::client::replies::reply_controller::sender_controller::SenderReplyController; pub(crate) use requests::{ReplyControllerMessage, ReplyControllerReceiver, ReplyControllerSender}; +pub mod key_rotation_helpers; +mod receiver_controller; pub mod requests; +mod sender_controller; -// this is still left as a separate config so I wouldn't need to replace it everywhere -// plus its not unreasonable to think that we might need something outside config::ReplySurbs struct +#[derive(Clone, Copy)] pub struct Config { reply_surbs: config::ReplySurbs, + + /// Current configuration value of the key rotation as setup on this network. + /// This includes things such as number of epochs per rotation, duration of epochs, etc. + // NOTE: this is operating on the assumption of constant-length epochs + key_rotation: KeyRotationConfig, } impl Config { - pub(crate) fn new(reply_surbs_cfg: config::ReplySurbs) -> Self { + pub(crate) fn new( + reply_surbs_cfg: config::ReplySurbs, + key_rotation: KeyRotationConfig, + ) -> Self { Self { reply_surbs: reply_surbs_cfg, + key_rotation, } } } @@ -50,651 +51,50 @@ impl Config { // - so I guess it will handle all 'RepliableMessage' and requests from 'ReplyMessage' // - replies to "give additional surbs" requests // - will reply to future heartbeats - pub type MaxRetransmissions = Option; -// TODO: this should be split into ingress and egress controllers -// because currently its trying to perform two distinct jobs pub struct ReplyController { config: Config, - // TODO: incorporate that field at some point - // and use binomial distribution to determine the expected required number - // of surbs required to send the message through - // expected_reliability: f32, + sender_controller: SenderReplyController, + receiver_controller: ReceiverReplyController, + request_receiver: ReplyControllerReceiver, - pending_replies: - HashMap>, - - /// Retransmission packets that have already timed out and are waiting for additional reply SURBs - /// so that they could be sent back to the network. Once we receive more SURBs, we should send them ASAP. - // TODO: when purging stale entries, we must take extra care to also purge all pending ACK data!! - pending_retransmissions: - HashMap>>, - - message_handler: MessageHandler, - full_reply_storage: CombinedReplyStorage, // Listen for shutdown signals task_client: TaskClient, } -impl ReplyController -where - R: CryptoRng + Rng, -{ +impl ReplyController { pub(crate) fn new( config: Config, - message_handler: MessageHandler, + message_handler: MessageHandler, full_reply_storage: CombinedReplyStorage, request_receiver: ReplyControllerReceiver, task_client: TaskClient, ) -> Self { ReplyController { config, + sender_controller: SenderReplyController::new( + config, + &full_reply_storage, + message_handler.clone(), + ), + receiver_controller: ReceiverReplyController::new( + config, + full_reply_storage.surbs_storage(), + message_handler, + ), request_receiver, - pending_replies: HashMap::new(), - pending_retransmissions: HashMap::new(), - message_handler, - full_reply_storage, task_client, } } +} - fn insert_pending_replies>( - &mut self, - recipient: &AnonymousSenderTag, - fragments: I, - lane: TransmissionLane, - ) { - trace!("buffering pending replies for {recipient}"); - self.pending_replies - .entry(*recipient) - .or_insert_with(TransmissionBuffer::new) - .store(&lane, fragments) - } - - fn re_insert_pending_replies( - &mut self, - recipient: &AnonymousSenderTag, - fragments: Vec<(TransmissionLane, FragmentWithMaxRetransmissions)>, - ) { - trace!("re-inserting pending replies for {recipient}"); - // the buffer should ALWAYS exist at this point, if it doesn't, it's a bug... - self.pending_replies - .entry(*recipient) - .or_insert_with(TransmissionBuffer::new) - .store_multiple(fragments) - } - - fn re_insert_pending_retransmission( - &mut self, - recipient: &AnonymousSenderTag, - data: Vec>, - ) { - trace!("re-inserting pending retransmissions for {recipient}"); - // the underlying entry MUST exist as we've just got data from there - let map_entry = self - .pending_retransmissions - .get_mut(recipient) - .expect("our pending retransmission entry is somehow gone!"); - - for pending in data { - // if it's 0, we don't need to do anything - we just got that ack! - if Arc::strong_count(&pending) > 1 { - let id = pending.inner_fragment_identifier(); - let downgraded = Arc::downgrade(&pending); - map_entry.insert(id, downgraded); - } - } - } - - fn should_request_more_surbs(&self, target: &AnonymousSenderTag) -> bool { - trace!("checking if we should request more surbs from {target}"); - - let pending_queue_size = self - .pending_replies - .get(target) - .map(|pending_queue| pending_queue.total_size()) - .unwrap_or_default(); - - let retransmission_queue = self - .pending_retransmissions - .get(target) - .map(|pending_queue| pending_queue.len()) - .unwrap_or_default(); - - let total_queue = pending_queue_size + retransmission_queue; - - let available_surbs = self - .full_reply_storage - .surbs_storage_ref() - .available_surbs(target); - let pending_surbs = self - .full_reply_storage - .surbs_storage_ref() - .pending_reception(target) as usize; - let min_surbs_threshold = self - .full_reply_storage - .surbs_storage_ref() - .min_surb_threshold(); - let max_surbs_threshold = self - .full_reply_storage - .surbs_storage_ref() - .max_surb_threshold(); - let min_surbs_threshold_buffer = - self.config.reply_surbs.minimum_reply_surb_threshold_buffer; - - // After clearing the queue, we want to have at least `min_surbs_threshold` surbs available - // and reserved for requesting additional surbs, and in addition to that we also want to - // have `min_surbs_threshold_buffer` surbs available proactively. - let target_surbs_after_clearing_queue = min_surbs_threshold + min_surbs_threshold_buffer; - - // Check if we have enough surbs to handle the total queue and maintain minimum thresholds - let total_required_surbs = total_queue + target_surbs_after_clearing_queue; - let total_available_surbs = pending_surbs + available_surbs; - - debug!("total queue size: {total_queue} = pending data {pending_queue_size} + pending retransmission {retransmission_queue}, available surbs: {available_surbs} pending surbs: {pending_surbs} threshold range: {min_surbs_threshold}..+{min_surbs_threshold_buffer}..{max_surbs_threshold}"); - - // We should request more surbs if: - // 1. We haven't hit the maximum surb threshold, and - // 2. We don't have enough surbs to handle the queue plus minimum thresholds - let is_below_max_threshold = total_available_surbs < max_surbs_threshold; - let is_below_required_surbs = total_available_surbs < total_required_surbs; - - is_below_max_threshold && is_below_required_surbs - } - - async fn handle_send_reply( - &mut self, - recipient_tag: AnonymousSenderTag, - data: Vec, - lane: TransmissionLane, - max_retransmissions: Option, - ) { - if !self - .full_reply_storage - .surbs_storage_ref() - .contains_surbs_for(&recipient_tag) - { - warn!("received reply request for {:?} but we don't have any surbs stored for that recipient!", recipient_tag); - return; - } - - trace!("handling reply to {:?}", recipient_tag); - let mut fragments = self.message_handler.split_reply_message(data); - let total_size = fragments.len(); - trace!("This reply requires {:?} SURBs", total_size); - - let available_surbs = self - .full_reply_storage - .surbs_storage_ref() - .available_surbs(&recipient_tag); - let min_surbs_threshold = self - .full_reply_storage - .surbs_storage_ref() - .min_surb_threshold(); - - let max_to_send = if available_surbs > min_surbs_threshold { - min(fragments.len(), available_surbs - min_surbs_threshold) - } else { - 0 - }; - - if max_to_send > 0 { - let (surbs, _surbs_left) = self - .full_reply_storage - .surbs_storage_ref() - .get_reply_surbs(&recipient_tag, max_to_send); - - if let Some(reply_surbs) = surbs { - let to_send = fragments - .drain(..max_to_send) - .map(|f| FragmentWithMaxRetransmissions { - fragment: f, - max_retransmissions, - }) - .collect::>(); - - if let Err(err) = self - .message_handler - .try_send_reply_chunks_on_lane( - recipient_tag, - to_send.clone(), - reply_surbs, - lane, - ) - .await - { - let err = err.return_unused_surbs( - self.full_reply_storage.surbs_storage_ref(), - &recipient_tag, - ); - warn!("failed to send reply to {recipient_tag}: {err}"); - info!( - "buffering {no_fragments} fragments for {recipient_tag}", - no_fragments = to_send.len() - ); - self.insert_pending_replies(&recipient_tag, to_send, lane); - } - } - } - - // if there's leftover data we didn't send because we didn't have enough (or any) surbs - buffer it - if !fragments.is_empty() { - // Ideally we should have enough surbs above the minimum threshold to handle sending - // new replies without having to first request more surbs. That's why I'd like to log - // these cases as they might indicate a problem with the surb management. - debug!( - "buffering {no_fragments} fragments for {recipient_tag}", - no_fragments = fragments.len() - ); - let fragments: Vec<_> = fragments - .into_iter() - .map(|fragment| FragmentWithMaxRetransmissions { - fragment, - max_retransmissions, - }) - .collect(); - self.insert_pending_replies(&recipient_tag, fragments, lane); - } - - if self.should_request_more_surbs(&recipient_tag) { - self.request_reply_surbs_for_queue_clearing(recipient_tag) - .await; - } - } - - async fn request_additional_reply_surbs( - &mut self, - target: AnonymousSenderTag, - amount: u32, - ) -> Result<(), PreparationError> { - debug!("requesting {amount} additional reply surbs for {target}"); - let reply_surb = self - .full_reply_storage - .surbs_storage_ref() - .get_reply_surb_ignoring_threshold(&target) - .and_then(|(reply_surb, _)| reply_surb) - .ok_or(PreparationError::NotEnoughSurbs { - available: 0, - required: 1, - })?; - - if let Err(err) = self - .message_handler - .try_request_additional_reply_surbs(target, reply_surb, amount) - .await - { - let err = err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target); - warn!( - "failed to request additional surbs from {:?} - {err}", - target - ); - return Err(err); - } else { - self.full_reply_storage - .surbs_storage_ref() - .increment_pending_reception(&target, amount); - } - - Ok(()) - } - - async fn try_clear_pending_retransmission(&mut self, target: AnonymousSenderTag) { - trace!("trying to clear pending retransmission queue"); - let available_surbs = self - .full_reply_storage - .surbs_storage_ref() - .available_surbs(&target); - let min_surbs_threshold = self - .full_reply_storage - .surbs_storage_ref() - .min_surb_threshold(); - - let max_to_clear = if available_surbs > min_surbs_threshold { - available_surbs - min_surbs_threshold - } else { - trace!("we don't have enough surbs for retransmission queue clearing..."); - return; - }; - trace!("we can clear up to {max_to_clear} entries"); - - let Some(pending) = self.pending_retransmissions.get_mut(&target) else { - trace!("there are no pending retransmissions for {target}!"); - return; - }; - - let mut to_take = Vec::new(); - - while to_take.len() < max_to_clear { - if let Some((_, data)) = pending.pop_first() { - // no need to do anything if we failed to upgrade the reference, - // it means we got the ack while the data was waiting in the queue - if let Some(upgraded) = data.upgrade() { - to_take.push(upgraded) - } - } else { - // our map is empty! - break; - } - } - - if to_take.is_empty() { - // no need to do anything - return; - } - - let (surbs_for_reply, _) = self - .full_reply_storage - .surbs_storage_ref() - .get_reply_surbs(&target, to_take.len()); - - let Some(surbs_for_reply) = surbs_for_reply else { - error!("somehow different task has stolen our reply surbs! - this should have been impossible"); - self.re_insert_pending_retransmission(&target, to_take); - return; - }; - - let to_send_vec = to_take.iter().map(|ack| ack.fragment_data()).collect(); - - let prepared_fragments = match self - .message_handler - .prepare_reply_chunks_for_sending(to_send_vec, surbs_for_reply) - .await - { - Ok(prepared) => prepared, - Err(err) => { - let err = - err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target); - self.re_insert_pending_retransmission(&target, to_take); - - warn!( - "failed to clear pending retransmission queue for {:?} - {err}", - target - ); - return; - } - }; - - // we can't fail at this point, so drop all references to acks so that timer updates wouldn't blow up - drop(to_take); - - self.message_handler - .send_retransmission_reply_chunks(prepared_fragments, TransmissionLane::Retransmission) - .await; - } - - fn pop_at_most_pending_replies( - &mut self, - from: &AnonymousSenderTag, - amount: usize, - ) -> Option> { - // if possible, pop all pending replies, if not, pop only entries for which we'd have a reply surb - let total = self.pending_replies.get(from)?.total_size(); - trace!("pending queue has {total} elements"); - if total == 0 { - return None; - } - self.pending_replies - .get_mut(from)? - .pop_at_most_n_next_messages_at_random(amount) - } - - async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) { - trace!("trying to clear pending queue"); - let available_surbs = self - .full_reply_storage - .surbs_storage_ref() - .available_surbs(&target); - let min_surbs_threshold = self - .full_reply_storage - .surbs_storage_ref() - .min_surb_threshold(); - - let max_to_clear = if available_surbs > min_surbs_threshold { - available_surbs - min_surbs_threshold - } else { - trace!("we don't have enough surbs for queue clearing..."); - return; - }; - trace!("we can clear up to {max_to_clear} entries"); - - // we're guaranteed to not get more entries than we have reply surbs for - if let Some(to_send) = self.pop_at_most_pending_replies(&target, max_to_clear) { - let to_send_clone = to_send.clone(); - - if to_send_clone.is_empty() { - panic!( - "please let the devs know if you ever see this message (reply_controller.rs)" - ); - } - - let (surbs_for_reply, _) = self - .full_reply_storage - .surbs_storage_ref() - .get_reply_surbs(&target, to_send_clone.len()); - - let Some(surbs_for_reply) = surbs_for_reply else { - error!("somehow different task has stolen our reply surbs! - this should have been impossible"); - self.re_insert_pending_replies(&target, to_send); - return; - }; - - if let Err(err) = self - .message_handler - .try_send_reply_chunks(target, to_send_clone, surbs_for_reply) - .await - { - let err = - err.return_unused_surbs(self.full_reply_storage.surbs_storage_ref(), &target); - self.re_insert_pending_replies(&target, to_send); - warn!("failed to clear pending queue for {:?} - {err}", target); - } - } else { - trace!("the pending queue is empty"); - } - } - - async fn handle_received_surbs( - &mut self, - from: AnonymousSenderTag, - reply_surbs: Vec, - from_surb_request: bool, - ) { - trace!("handling received surbs"); - - // clear the requesting flag since we should have been asking for surbs - self.full_reply_storage - .surbs_storage_ref() - .reset_surbs_last_received_at(&from); - if from_surb_request { - self.full_reply_storage - .surbs_storage_ref() - .decrement_pending_reception(&from, reply_surbs.len() as u32); - } - - // store received surbs - self.full_reply_storage - .surbs_storage_ref() - .insert_surbs(&from, reply_surbs); - - // use as many as we can for clearing pending retransmission queue - self.try_clear_pending_retransmission(from).await; - - // use as many as we can for clearing pending 'normal' queue - self.try_clear_pending_queue(from).await; - - // if we have to, request more - if self.should_request_more_surbs(&from) { - self.request_reply_surbs_for_queue_clearing(from).await; - } - } - - async fn handle_surb_request(&mut self, recipient: Recipient, mut amount: u32) { - // 1. check whether we sent any surbs in the past to this recipient, otherwise - // they have no business in asking for more - if !self - .full_reply_storage - .tags_storage_ref() - .exists(&recipient) - { - warn!("{recipient} asked us for reply SURBs even though we never sent them any anonymous messages before!"); - return; - } - - // 2. check whether the requested amount is within sane range - if amount - > self - .config - .reply_surbs - .maximum_allowed_reply_surb_request_size - { - warn!("The requested reply surb amount is larger than our maximum allowed ({amount} > {}). Lowering it to a more sane value...", self.config.reply_surbs.maximum_allowed_reply_surb_request_size); - amount = self - .config - .reply_surbs - .maximum_allowed_reply_surb_request_size; - } - - // 3. construct and send the surbs away - // (send them in smaller batches to make the experience a bit smoother - let mut remaining = amount; - while remaining > 0 { - let to_send = min(remaining, 100); - if let Err(err) = self - .message_handler - .try_send_additional_reply_surbs( - recipient, - to_send, - nym_sphinx::params::PacketType::Mix, - ) - .await - { - warn!("failed to send additional surbs to {recipient} - {err}"); - } else { - trace!("sent {to_send} reply SURBs to {recipient}"); - } - - remaining -= to_send; - } - } - - fn buffer_pending_ack( - &mut self, - recipient: AnonymousSenderTag, - ack_ref: Arc, - weak_ack_ref: Weak, - ) { - let frag_id = ack_ref.inner_fragment_identifier(); - if let Some(existing) = self.pending_retransmissions.get_mut(&recipient) { - if let Entry::Vacant(e) = existing.entry(frag_id) { - e.insert(weak_ack_ref); - } else { - warn!("we're already trying to retransmit {frag_id}. We must be really behind in surbs!"); - } - } else { - let mut inner = BTreeMap::new(); - inner.insert(frag_id, weak_ack_ref); - self.pending_retransmissions.insert(recipient, inner); - } - } - - async fn handle_reply_retransmission( - &mut self, - recipient_tag: AnonymousSenderTag, - timed_out_ack: Weak, - extra_surbs_request: bool, - ) { - // seems we got the ack in the end - let ack_ref = match timed_out_ack.upgrade() { - Some(ack) => ack, - None => { - debug!("we received the ack for one of the reply packets as we were putting it in the retransmission queue"); - return; - } - }; - - // if this is retransmission for obtaining additional reply surbs, - // we can dip below the storage threshold - let (maybe_reply_surb, _) = if extra_surbs_request { - self.full_reply_storage - .surbs_storage_ref() - .get_reply_surb_ignoring_threshold(&recipient_tag) - } else { - self.full_reply_storage - .surbs_storage_ref() - .get_reply_surb(&recipient_tag) - } - .expect("attempted to retransmit a packet to an unknown recipient - we shouldn't have sent the original packet in the first place!"); - - if let Some(reply_surb) = maybe_reply_surb { - match self - .message_handler - .try_prepare_single_reply_chunk_for_sending(reply_surb, ack_ref.fragment_data()) - .await - { - Ok(prepared) => { - // drop the ack ref so that controller would not panic on `UpdateTimer` if that task - // got to handle the action before this function terminated (which is very much - // possible if `forward_messages` takes a while) - drop(ack_ref); - - self.message_handler - .update_ack_delay(prepared.fragment_identifier, prepared.total_delay); - self.message_handler - .forward_messages(vec![prepared.into()], TransmissionLane::Retransmission) - .await; - } - Err(err) => { - let err = err.return_unused_surbs( - self.full_reply_storage.surbs_storage_ref(), - &recipient_tag, - ); - warn!("failed to prepare message for retransmission - {err}"); - // we buffer that packet and to try another day - self.buffer_pending_ack(recipient_tag, ack_ref, timed_out_ack); - - if self.should_request_more_surbs(&recipient_tag) { - self.request_reply_surbs_for_queue_clearing(recipient_tag) - .await; - } - } - }; - } else { - self.buffer_pending_ack(recipient_tag, ack_ref, timed_out_ack); - - if self.should_request_more_surbs(&recipient_tag) { - self.request_reply_surbs_for_queue_clearing(recipient_tag) - .await; - } - } - } - - // to be honest this doesn't make a lot of sense in the context of `connection_id`, - // it should really be asked per tag - fn handle_lane_queue_length( - &self, - connection_id: ConnectionId, - response_channel: oneshot::Sender, - ) { - // TODO: if we ever have duplicate ids for different senders, it means our rng is super weak - // thus I don't think we have to worry about it? - let lane = TransmissionLane::ConnectionId(connection_id); - for buf in self.pending_replies.values() { - if let Some(length) = buf.lane_length(&lane) { - if response_channel.send(length).is_err() { - error!("the requester for lane queue length has dropped the response channel!") - } - return; - } - } - // make sure that if we didn't find that lane, we reply with 0 - if response_channel.send(0).is_err() { - error!("the requester for lane queue length has dropped the response channel!") - } - } - +impl ReplyController +where + R: CryptoRng + Rng, +{ async fn handle_request(&mut self, request: ReplyControllerMessage) { match request { ReplyControllerMessage::RetransmitReply { @@ -702,7 +102,8 @@ where timed_out_ack, extra_surb_request, } => { - self.handle_reply_retransmission(recipient, timed_out_ack, extra_surb_request) + self.receiver_controller + .handle_reply_retransmission(recipient, timed_out_ack, extra_surb_request) .await } ReplyControllerMessage::SendReply { @@ -711,7 +112,8 @@ where lane, max_retransmissions, } => { - self.handle_send_reply(recipient, message, lane, max_retransmissions) + self.receiver_controller + .handle_send_reply(recipient, message, lane, max_retransmissions) .await } ReplyControllerMessage::AdditionalSurbs { @@ -719,225 +121,67 @@ where reply_surbs, from_surb_request, } => { - self.handle_received_surbs(sender_tag, reply_surbs, from_surb_request) + self.receiver_controller + .handle_received_surbs(sender_tag, reply_surbs, from_surb_request) .await } ReplyControllerMessage::LaneQueueLength { connection_id, response_channel, - } => self.handle_lane_queue_length(connection_id, response_channel), + } => self + .receiver_controller + .handle_lane_queue_length(connection_id, response_channel), ReplyControllerMessage::AdditionalSurbsRequest { recipient, amount } => { - self.handle_surb_request(*recipient, amount).await + self.sender_controller + .handle_surb_request(*recipient, amount) + .await } } } - // TODO: modify this method to more accurately determine the amount of surbs it needs to request - // it should take into consideration the average latency, sending rate and queue size. - // it should request as many surbs as it takes to saturate its sending rate before next batch arrives - async fn request_reply_surbs_for_queue_clearing(&mut self, target: AnonymousSenderTag) { - trace!("requesting surbs for queue clearing"); - - let pending_queue_size = self - .pending_replies - .get(&target) - .map(|pending_queue| pending_queue.total_size()) - .unwrap_or_default(); - - let retransmission_queue = self - .pending_retransmissions - .get(&target) - .map(|pending_queue| pending_queue.len()) - .unwrap_or_default(); - - let min_surbs_buffer = self.config.reply_surbs.minimum_reply_surb_threshold_buffer as u32; - - let total_queue = (pending_queue_size + retransmission_queue) as u32; - - // To proactively request additional surbs, we aim to have a buffer of extra surbs in our - // storage. - let total_queue_with_buffer = total_queue + min_surbs_buffer; - - let request_size = min( - self.config.reply_surbs.maximum_reply_surb_request_size, - max( - total_queue_with_buffer, - self.config.reply_surbs.minimum_reply_surb_request_size, - ), - ); - - if let Err(err) = self - .request_additional_reply_surbs(target, request_size) - .await - { - info!("{err}") - } - } - - async fn inspect_stale_entries(&mut self) { - let mut to_request = Vec::new(); - let mut to_remove = Vec::new(); - - let now = OffsetDateTime::now_utc(); - for (pending_reply_target, vals) in &self.pending_replies { - if vals.is_empty() { - continue; - } - - let Some(last_received) = self - .full_reply_storage - .surbs_storage_ref() - .surbs_last_received_at(pending_reply_target) - else { - error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", vals.total_size()); - to_remove.push(*pending_reply_target); - continue; - }; - - // this should never ever happen (famous last words, eh?), but in case it DOES happen eventually - // purge that malformed data - let Ok(last_received_time) = OffsetDateTime::from_unix_timestamp(last_received) else { - error!("somehow our stored timestamp ({last_received}) for surbs from {pending_reply_target} is corrupted!. Going to remove all the associated entries"); - to_remove.push(*pending_reply_target); - continue; - }; - - let diff = now - last_received_time; - let max_rerequest_wait = self - .config - .reply_surbs - .maximum_reply_surb_rerequest_waiting_period; - let max_drop_wait = self - .config - .reply_surbs - .maximum_reply_surb_drop_waiting_period; - - if diff > max_rerequest_wait { - if diff > max_drop_wait { - to_remove.push(*pending_reply_target) - } else { - debug!("We haven't received any surbs in {:?} from {pending_reply_target}. Going to explicitly ask for more", diff); - to_request.push(*pending_reply_target); - } - } - } - - for pending_reply_target in to_request { - self.request_reply_surbs_for_queue_clearing(pending_reply_target) - .await; - self.full_reply_storage - .surbs_storage_ref() - .reset_pending_reception(&pending_reply_target) - } - for to_remove in to_remove { - self.pending_replies.remove(&to_remove); - } - } - - async fn invalidate_old_data(&self) { + async fn remove_stale_storage(&mut self) { let now = OffsetDateTime::now_utc(); - let mut to_remove_surbs = Vec::new(); - let mut to_remove_keys = Vec::new(); - for map_ref in self.full_reply_storage.surbs_storage_ref().as_raw_iter() { - let (sender, received) = map_ref.pair(); - // TODO: handle the following edge case: - // there's a malicious client sending us exactly one reply surb just before we should have invalidated - // the data thus making us keep everything in memory - // possible solution: keep timestamp PER reply surb (but that seems like an overkill) - // but I doubt this is ever going to be a problem... - // ... - // However, if you're reading this message, it probably became a legit problem, - // so I guess add timestamp per surb then? chop-chop. - - let last_received = received.surbs_last_received_at(); - // this should never ever happen (famous last words, eh?), but in case it DOES happen eventually - // purge that malformed data - let Ok(last_received_time) = OffsetDateTime::from_unix_timestamp(last_received) else { - error!("somehow our stored timestamp ({last_received}) for surbs from {sender} is corrupted!. Going to remove all the associated entries"); - to_remove_surbs.push(*sender); - continue; - }; - let diff = now - last_received_time; - - if diff > self.config.reply_surbs.maximum_reply_surb_age { - info!("it's been {diff:?} since we last received any reply surb from {sender}. Going to remove all stored entries..."); - - to_remove_surbs.push(*sender); - } - } - - for map_ref in self.full_reply_storage.key_storage_ref().as_raw_iter() { - let (digest, reply_key) = map_ref.pair(); - - // this should never ever happen (famous last words, eh?), but in case it DOES happen eventually - // purge that malformed data - let Ok(sent_at) = OffsetDateTime::from_unix_timestamp(reply_key.sent_at_timestamp) - else { - error!("somehow our stored timestamp ({}) for one of our reply key is corrupted!. Going to remove all the entry", reply_key.sent_at_timestamp); - to_remove_keys.push(*digest); - continue; - }; - - let diff = now - sent_at; - - if diff > self.config.reply_surbs.maximum_reply_key_age { - debug!("it's been {diff:?} since we created this reply key. it's probably never going to get used, so we're going to purge it..."); - to_remove_keys.push(*digest); - } - } - - for to_remove in to_remove_surbs { - self.full_reply_storage - .surbs_storage_ref() - .remove(&to_remove); - } - - for to_remove in to_remove_keys { - self.full_reply_storage.key_storage().remove(to_remove) - } + self.receiver_controller + .inspect_and_clear_stale_data(now) + .await; + self.sender_controller.inspect_and_clear_stale_data(now) } - // #[cfg(not(target_arch = "wasm32"))] - // async fn log_status(&self) { - // todo!() - // } - pub(crate) async fn run(&mut self) { debug!("Started ReplyController with graceful shutdown support"); - let mut shutdown = self.task_client.fork("select"); + let mut shutdown = self.task_client.fork("reply-controller"); let polling_rate = Duration::from_secs(5); let mut stale_inspection = new_interval_stream(polling_rate); - // this is in the order of hours/days so we don't have to poll it that often - let polling_rate = - Duration::from_secs(self.config.reply_surbs.maximum_reply_surb_age.as_secs() / 10); + let polling_rate = self.config.key_rotation.epoch_duration / 8; let mut invalidation_inspection = new_interval_stream(polling_rate); while !shutdown.is_shutdown() { tokio::select! { biased; _ = shutdown.recv() => { - log::trace!("ReplyController: Received shutdown"); + tracing::trace!("ReplyController: Received shutdown"); }, req = self.request_receiver.next() => match req { Some(req) => self.handle_request(req).await, None => { - log::trace!("ReplyController: Stopping since channel closed"); + tracing::trace!("ReplyController: Stopping since channel closed"); break; } }, _ = stale_inspection.next() => { - self.inspect_stale_entries().await + self.receiver_controller.inspect_stale_pending_data().await }, _ = invalidation_inspection.next() => { - self.invalidate_old_data().await + self.receiver_controller.check_surb_refresh().await; + self.remove_stale_storage().await; } } } assert!(shutdown.is_shutdown_poll()); - log::debug!("ReplyController: Exiting"); + tracing::debug!("ReplyController: Exiting"); } } diff --git a/common/client-core/src/client/replies/reply_controller/receiver_controller.rs b/common/client-core/src/client/replies/reply_controller/receiver_controller.rs new file mode 100644 index 0000000000..d8bbce9aac --- /dev/null +++ b/common/client-core/src/client/replies/reply_controller/receiver_controller.rs @@ -0,0 +1,899 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement; +use crate::client::real_messages_control::message_handler::{ + FragmentWithMaxRetransmissions, MessageHandler, PreparationError, +}; +use crate::client::replies::reply_controller::key_rotation_helpers::SurbRefreshState; +use crate::client::replies::reply_controller::Config; +use crate::client::topology_control::TopologyAccessor; +use crate::client::transmission_buffer::TransmissionBuffer; +use futures::channel::oneshot; +use nym_client_core_surb_storage::{ReceivedReplySurb, ReceivedReplySurbsMap}; +use nym_crypto::aes::cipher::crypto_common::rand_core::CryptoRng; +use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; +use nym_sphinx::anonymous_replies::ReplySurbWithKeyRotation; +use nym_sphinx::chunking::fragment::FragmentIdentifier; +use nym_task::connections::{ConnectionId, TransmissionLane}; +use nym_topology::NymTopologyMetadata; +use rand::Rng; +use std::cmp::{max, min}; +use std::collections::btree_map::Entry; +use std::collections::{BTreeMap, HashMap}; +use std::mem; +use std::sync::{Arc, Weak}; +use time::OffsetDateTime; +use tracing::{debug, error, info, trace, warn}; + +struct SenderData { + current_clear_rerequest_counter: usize, + pending_replies: TransmissionBuffer, + pending_retransmissions: BTreeMap>, + last_request_failure: OffsetDateTime, +} + +impl Default for SenderData { + fn default() -> Self { + SenderData { + current_clear_rerequest_counter: 0, + pending_replies: Default::default(), + pending_retransmissions: Default::default(), + last_request_failure: OffsetDateTime::UNIX_EPOCH, + } + } +} + +impl SenderData { + fn total_pending(&self) -> usize { + let pending_replies = self.pending_replies.total_size(); + let pending_retransmissions = self.pending_retransmissions.len(); + let total_pending = pending_retransmissions + pending_replies; + + debug!("total queue size: {total_pending} = pending data {pending_replies} + pending retransmission {pending_retransmissions}"); + + total_pending + } + + pub(crate) fn increment_current_clear_rerequest_counter(&mut self) { + self.current_clear_rerequest_counter += 1; + } + + pub(crate) fn reset_current_clear_rerequest_counter(&mut self) { + self.current_clear_rerequest_counter = 0; + } + + pub(crate) fn reset_last_request_failure(&mut self, now: OffsetDateTime) -> OffsetDateTime { + mem::replace(&mut self.last_request_failure, now) + } +} + +/// Reply controller responsible for controlling receiver-related part +/// of replies, such as requesting additional reply SURBs +pub struct ReceiverReplyController { + config: Config, + + surb_refresh_state: SurbRefreshState, + topology_access: TopologyAccessor, + + surb_senders: HashMap, + unavailable: HashMap, + surbs_storage: ReceivedReplySurbsMap, + + // TODO: incorporate that field at some point + // and use binomial distribution to determine the expected required number + // of surbs required to send the message through + // expected_reliability: f32, + message_handler: MessageHandler, +} + +impl ReceiverReplyController +where + R: CryptoRng + Rng, +{ + pub(crate) fn new( + config: Config, + storage: ReceivedReplySurbsMap, + message_handler: MessageHandler, + ) -> Self { + let topology_access = message_handler.topology_access_handle().clone(); + + ReceiverReplyController { + config, + surb_refresh_state: SurbRefreshState::WaitingForNextRotation { + last_known: config + .key_rotation + .expected_current_key_rotation_id(OffsetDateTime::now_utc()), + }, + topology_access, + surb_senders: Default::default(), + unavailable: Default::default(), + surbs_storage: storage, + message_handler, + } + } + + fn get_or_create_surb_sender(&mut self, tag: &AnonymousSenderTag) -> &mut SenderData { + self.surb_senders.entry(*tag).or_default() + } + + async fn current_topology_metadata(&self) -> Option { + self.topology_access.current_metadata().await + } + + fn insert_pending_replies>( + &mut self, + recipient: &AnonymousSenderTag, + fragments: I, + lane: TransmissionLane, + ) { + trace!("buffering pending replies for {recipient}"); + self.surb_senders + .entry(*recipient) + .or_default() + .pending_replies + .store(&lane, fragments) + } + + fn re_insert_pending_replies( + &mut self, + recipient: &AnonymousSenderTag, + fragments: Vec<(TransmissionLane, FragmentWithMaxRetransmissions)>, + ) { + trace!("re-inserting pending replies for {recipient}"); + // the buffer should ALWAYS exist at this point, if it doesn't, it's a bug... + self.surb_senders + .entry(*recipient) + .or_default() + .pending_replies + .store_multiple(fragments) + } + + fn re_insert_pending_retransmission( + &mut self, + recipient: &AnonymousSenderTag, + data: Vec>, + ) { + trace!("re-inserting pending retransmissions for {recipient}"); + // the underlying entry MUST exist as we've just got data from there + // and we hold a mut reference + let map_entry = &mut self + .surb_senders + .get_mut(recipient) + .expect("our pending retransmission entry is somehow gone!") + .pending_retransmissions; + + for pending in data { + // if it's 0, we don't need to do anything - we just got that ack! + if Arc::strong_count(&pending) > 1 { + let id = pending.inner_fragment_identifier(); + let downgraded = Arc::downgrade(&pending); + map_entry.insert(id, downgraded); + } + } + } + + fn should_request_more_surbs(&self, target: &AnonymousSenderTag) -> bool { + trace!("checking if we should request more surbs from {target}"); + + let total_queue = self + .surb_senders + .get(target) + .map(|pending| pending.total_pending()) + .unwrap_or_default(); + + // only consider 'fresh' surbs + let available_surbs = self.surbs_storage.available_fresh_surbs(target); + let pending_surbs = self.surbs_storage.pending_reception(target) as usize; + let min_surbs_threshold = self.surbs_storage.min_surb_threshold(); + let max_surbs_threshold = self.surbs_storage.max_surb_threshold(); + let min_surbs_threshold_buffer = + self.config.reply_surbs.minimum_reply_surb_threshold_buffer; + + // After clearing the queue, we want to have at least `min_surbs_threshold` surbs available + // and reserved for requesting additional surbs, and in addition to that we also want to + // have `min_surbs_threshold_buffer` surbs available proactively. + let target_surbs_after_clearing_queue = min_surbs_threshold + min_surbs_threshold_buffer; + + // Check if we have enough surbs to handle the total queue and maintain minimum thresholds + let total_required_surbs = total_queue + target_surbs_after_clearing_queue; + let total_available_surbs = pending_surbs + available_surbs; + + debug!("available surbs: {available_surbs} pending surbs: {pending_surbs} threshold range: {min_surbs_threshold}..+{min_surbs_threshold_buffer}..{max_surbs_threshold}"); + + // We should request more surbs if: + // 1. We haven't hit the maximum surb threshold, and + // 2. We don't have enough surbs to handle the queue plus minimum thresholds + let is_below_max_threshold = total_available_surbs < max_surbs_threshold; + let is_below_required_surbs = total_available_surbs < total_required_surbs; + + is_below_max_threshold && is_below_required_surbs + } + + pub(crate) async fn handle_send_reply( + &mut self, + recipient_tag: AnonymousSenderTag, + data: Vec, + lane: TransmissionLane, + max_retransmissions: Option, + ) { + if !self.surbs_storage.contains_surbs_for(&recipient_tag) { + if self + .unavailable + .insert(recipient_tag, OffsetDateTime::now_utc()) + .is_none() + { + // don't report it every single time + warn!("received reply request for {recipient_tag} but we don't have any surbs stored for that recipient!"); + } else { + trace!("received reply request for {recipient_tag} but we don't have any surbs stored for that recipient!"); + } + return; + } + + trace!("handling reply to {recipient_tag}"); + let mut fragments = self.message_handler.split_reply_message(data); + let total_size = fragments.len(); + trace!("This reply requires {total_size} SURBs"); + + // for the purposes of sending reply, do allow using possibly stale entries + let available_surbs = self.surbs_storage.available_surbs(&recipient_tag); + let min_surbs_threshold = self.surbs_storage.min_surb_threshold(); + + let max_to_send = if available_surbs > min_surbs_threshold { + min(fragments.len(), available_surbs - min_surbs_threshold) + } else { + 0 + }; + + if max_to_send > 0 { + let (surbs, surbs_left) = self + .surbs_storage + .get_reply_surbs(&recipient_tag, max_to_send); + + debug!( + "retrieved {} reply surbs. {surbs_left} surbs remaining in storage", + surbs.as_ref().map(|s| s.len()).unwrap_or_default() + ); + if let Some(reply_surbs) = surbs { + let to_send = fragments + .drain(..reply_surbs.len()) + .map(|f| FragmentWithMaxRetransmissions { + fragment: f, + max_retransmissions, + }) + .collect::>(); + + if let Err(err) = self + .message_handler + .try_send_reply_chunks_on_lane( + recipient_tag, + to_send.clone(), + reply_surbs, + lane, + ) + .await + { + let err = err.return_unused_surbs(&self.surbs_storage, &recipient_tag); + warn!("failed to send reply to {recipient_tag}: {err}"); + info!( + "buffering {no_fragments} fragments for {recipient_tag}", + no_fragments = to_send.len() + ); + self.insert_pending_replies(&recipient_tag, to_send, lane); + } + } + } + + // if there's leftover data we didn't send because we didn't have enough (or any) surbs - buffer it + if !fragments.is_empty() { + // Ideally we should have enough surbs above the minimum threshold to handle sending + // new replies without having to first request more surbs. That's why I'd like to log + // these cases as they might indicate a problem with the surb management. + debug!( + "buffering {no_fragments} fragments for {recipient_tag}", + no_fragments = fragments.len() + ); + let fragments: Vec<_> = fragments + .into_iter() + .map(|fragment| FragmentWithMaxRetransmissions { + fragment, + max_retransmissions, + }) + .collect(); + self.insert_pending_replies(&recipient_tag, fragments, lane); + } + + if self.should_request_more_surbs(&recipient_tag) { + self.request_reply_surbs_for_queue_clearing(recipient_tag) + .await; + } + } + + async fn request_additional_reply_surbs( + &mut self, + target: AnonymousSenderTag, + amount: u32, + ) -> Result<(), PreparationError> { + debug!("requesting {amount} additional reply surbs for {target}"); + let (reply_surb, _) = self + .surbs_storage + .get_reply_surb_ignoring_threshold(&target); + + let reply_surb = reply_surb.ok_or(PreparationError::NotEnoughSurbs { + available: 0, + required: 1, + })?; + + if let Err(err) = self + .message_handler + .try_request_additional_reply_surbs(target, reply_surb, amount) + .await + { + let err = err.return_unused_surbs(&self.surbs_storage, &target); + warn!("failed to request additional surbs from {target}: {err}",); + return Err(err); + } else { + self.surbs_storage + .increment_pending_reception(&target, amount); + } + + Ok(()) + } + + async fn try_clear_pending_retransmission(&mut self, target: AnonymousSenderTag) { + trace!("trying to clear pending retransmission queue"); + let available_surbs = self.surbs_storage.available_surbs(&target); + let min_surbs_threshold = self.surbs_storage.min_surb_threshold(); + + let max_to_clear = if available_surbs > min_surbs_threshold { + available_surbs - min_surbs_threshold + } else { + trace!("we don't have enough surbs for retransmission queue clearing..."); + return; + }; + trace!("we can clear up to {max_to_clear} entries"); + + let Some(pending) = self.surb_senders.get_mut(&target) else { + trace!("no pending entry for {target}!"); + return; + }; + + let mut to_take = Vec::new(); + + while to_take.len() < max_to_clear { + if let Some((_, data)) = pending.pending_retransmissions.pop_first() { + // no need to do anything if we failed to upgrade the reference, + // it means we got the ack while the data was waiting in the queue + if let Some(upgraded) = data.upgrade() { + to_take.push(upgraded) + } + } else { + // our map is empty! + break; + } + } + + if to_take.is_empty() { + // no need to do anything + return; + } + + let (surbs_for_reply, _) = self.surbs_storage.get_reply_surbs(&target, to_take.len()); + + let Some(surbs_for_reply) = surbs_for_reply else { + error!("somehow different task has stolen our reply surbs! - this should have been impossible"); + self.re_insert_pending_retransmission(&target, to_take); + return; + }; + + let to_send_vec = to_take.iter().map(|ack| ack.fragment_data()).collect(); + + let prepared_fragments = match self + .message_handler + .prepare_reply_chunks_for_sending(to_send_vec, surbs_for_reply) + .await + { + Ok(prepared) => prepared, + Err(err) => { + let err = err.return_unused_surbs(&self.surbs_storage, &target); + self.re_insert_pending_retransmission(&target, to_take); + + warn!("failed to clear pending retransmission queue for {target}: {err}",); + return; + } + }; + + // we can't fail at this point, so drop all references to acks so that timer updates wouldn't blow up + drop(to_take); + + self.message_handler + .send_retransmission_reply_chunks(prepared_fragments, TransmissionLane::Retransmission) + .await; + } + + fn pop_at_most_pending_replies( + &mut self, + from: &AnonymousSenderTag, + amount: usize, + ) -> Option> { + // if possible, pop all pending replies, if not, pop only entries for which we'd have a reply surb + let pending = self.surb_senders.get_mut(from)?; + let total = pending.pending_replies.total_size(); + trace!("pending queue has {total} elements"); + if total == 0 { + return None; + } + pending + .pending_replies + .pop_at_most_n_next_messages_at_random(amount) + } + + async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) { + trace!("trying to clear pending queue"); + let available_surbs = self.surbs_storage.available_surbs(&target); + let min_surbs_threshold = self.surbs_storage.min_surb_threshold(); + + let max_to_clear = if available_surbs > min_surbs_threshold { + available_surbs - min_surbs_threshold + } else { + trace!("we don't have enough surbs for queue clearing..."); + return; + }; + trace!("we can clear up to {max_to_clear} entries"); + + // we're guaranteed to not get more entries than we have reply surbs for + if let Some(to_send) = self.pop_at_most_pending_replies(&target, max_to_clear) { + let to_send_clone = to_send.clone(); + + if to_send_clone.is_empty() { + panic!( + "please let the devs know if you ever see this message (reply_controller.rs)" + ); + } + + let (surbs_for_reply, _) = self + .surbs_storage + .get_reply_surbs(&target, to_send_clone.len()); + + let Some(surbs_for_reply) = surbs_for_reply else { + error!("somehow different task has stolen our reply surbs! - this should have been impossible"); + self.re_insert_pending_replies(&target, to_send); + return; + }; + + if let Err(err) = self + .message_handler + .try_send_reply_chunks(target, to_send_clone, surbs_for_reply) + .await + { + let err = err.return_unused_surbs(&self.surbs_storage, &target); + self.re_insert_pending_replies(&target, to_send); + warn!("failed to clear pending queue for {target}: {err}"); + } + } else { + trace!("the pending queue is empty"); + } + } + + fn reset_rerequest_counter(&mut self, from: &AnonymousSenderTag) { + if let Some(pending) = self.surb_senders.get_mut(from) { + pending.reset_current_clear_rerequest_counter() + } + } + + pub(crate) async fn handle_received_surbs( + &mut self, + from: AnonymousSenderTag, + reply_surbs: Vec, + from_surb_request: bool, + ) { + trace!("handling received surbs"); + + // clear the requesting flag since we should have been asking for surbs + if from_surb_request { + self.surbs_storage + .decrement_pending_reception(&from, reply_surbs.len() as u32); + } + + // store received surbs + self.surbs_storage.insert_fresh_surbs(&from, reply_surbs); + + // reset, if applicable, request counter + self.reset_rerequest_counter(&from); + + // use as many as we can for clearing pending retransmission queue + self.try_clear_pending_retransmission(from).await; + + // use as many as we can for clearing pending 'normal' queue + self.try_clear_pending_queue(from).await; + + // if we have to, request more + if self.should_request_more_surbs(&from) { + self.request_reply_surbs_for_queue_clearing(from).await; + } + } + fn buffer_pending_ack( + &mut self, + recipient: AnonymousSenderTag, + ack_ref: Arc, + weak_ack_ref: Weak, + ) { + let frag_id = ack_ref.inner_fragment_identifier(); + + let pending = self.surb_senders.entry(recipient).or_default(); + if let Entry::Vacant(e) = pending.pending_retransmissions.entry(frag_id) { + e.insert(weak_ack_ref); + } else { + warn!( + "we're already trying to retransmit {frag_id}. We must be really behind in surbs!" + ); + } + } + + pub(crate) async fn handle_reply_retransmission( + &mut self, + recipient_tag: AnonymousSenderTag, + timed_out_ack: Weak, + extra_surbs_request: bool, + ) { + // seems we got the ack in the end + let ack_ref = match timed_out_ack.upgrade() { + Some(ack) => ack, + None => { + debug!("we received the ack for one of the reply packets as we were putting it in the retransmission queue"); + return; + } + }; + + // if this is retransmission for obtaining additional reply surbs, + // we can dip below the storage threshold + let (maybe_reply_surb, _) = if extra_surbs_request { + self.surbs_storage + .get_reply_surb_ignoring_threshold(&recipient_tag) + } else { + self.surbs_storage.get_reply_surb(&recipient_tag) + }; + + if let Some(reply_surb) = maybe_reply_surb { + match self + .message_handler + .try_prepare_single_reply_chunk_for_sending(reply_surb, ack_ref.fragment_data()) + .await + { + Ok(prepared) => { + // drop the ack ref so that controller would not panic on `UpdateTimer` if that task + // got to handle the action before this function terminated (which is very much + // possible if `forward_messages` takes a while) + drop(ack_ref); + + self.message_handler + .update_ack_delay(prepared.fragment_identifier, prepared.total_delay); + self.message_handler + .forward_messages(vec![prepared.into()], TransmissionLane::Retransmission) + .await; + } + Err(err) => { + let err = err.return_unused_surbs(&self.surbs_storage, &recipient_tag); + warn!("failed to prepare message for retransmission - {err}"); + // we buffer that packet and to try another day + self.buffer_pending_ack(recipient_tag, ack_ref, timed_out_ack); + + if self.should_request_more_surbs(&recipient_tag) { + self.request_reply_surbs_for_queue_clearing(recipient_tag) + .await; + } + } + }; + } else { + self.buffer_pending_ack(recipient_tag, ack_ref, timed_out_ack); + + if self.should_request_more_surbs(&recipient_tag) { + self.request_reply_surbs_for_queue_clearing(recipient_tag) + .await; + } + } + } + + // to be honest this doesn't make a lot of sense in the context of `connection_id`, + // it should really be asked per tag + pub(crate) fn handle_lane_queue_length( + &self, + connection_id: ConnectionId, + response_channel: oneshot::Sender, + ) { + // TODO: if we ever have duplicate ids for different senders, it means our rng is super weak + // thus I don't think we have to worry about it? + let lane = TransmissionLane::ConnectionId(connection_id); + for buf in self.surb_senders.values().map(|p| &p.pending_replies) { + if let Some(length) = buf.lane_length(&lane) { + if response_channel.send(length).is_err() { + error!("the requester for lane queue length has dropped the response channel!") + } + return; + } + } + // make sure that if we didn't find that lane, we reply with 0 + if response_channel.send(0).is_err() { + error!("the requester for lane queue length has dropped the response channel!") + } + } + + // TODO: modify this method to more accurately determine the amount of surbs it needs to request + // it should take into consideration the average latency, sending rate and queue size. + // it should request as many surbs as it takes to saturate its sending rate before next batch arrives + async fn request_reply_surbs_for_queue_clearing(&mut self, target: AnonymousSenderTag) { + trace!("requesting surbs for queue clearing"); + + let total_queue = self + .surb_senders + .get(&target) + .map(|pending| pending.total_pending() as u32) + .unwrap_or_default(); + + let min_surbs_buffer = self.config.reply_surbs.minimum_reply_surb_threshold_buffer as u32; + + // To proactively request additional surbs, we aim to have a buffer of extra surbs in our + // storage. + let total_queue_with_buffer = total_queue + min_surbs_buffer; + + let request_size = min( + self.config.reply_surbs.maximum_reply_surb_request_size, + max( + total_queue_with_buffer, + self.config.reply_surbs.minimum_reply_surb_request_size, + ), + ); + + if let Err(err) = self + .request_additional_reply_surbs(target, request_size) + .await + { + let now = OffsetDateTime::now_utc(); + let sender_info = self.get_or_create_surb_sender(&target); + let last_failure = sender_info.reset_last_request_failure(now); + + // only log at higher level if it's the first time this error has occurred in a while + if now - last_failure > time::Duration::seconds(30) { + warn!("failed to request more surbs to clear pending queue of size {total_queue} (attempted to request: {request_size}): {err}") + } else { + debug!("failed to request more surbs to clear pending queue of size {total_queue} (attempted to request: {request_size}): {err}") + } + } + } + + pub(crate) async fn inspect_stale_pending_data(&mut self) { + let mut to_request = Vec::new(); + let mut to_remove = Vec::new(); + + let now = OffsetDateTime::now_utc(); + for (pending_reply_target, vals) in self.surb_senders.iter_mut() { + // for now recreate old behaviour + let retransmission_buf = &vals.pending_replies; + + if retransmission_buf.is_empty() { + continue; + } + + let Some(last_received_time) = self + .surbs_storage + .surbs_last_received_at(pending_reply_target) + else { + error!("we have {} pending replies for {pending_reply_target}, but we somehow never received any reply surbs from them!", retransmission_buf.total_size()); + to_remove.push(*pending_reply_target); + continue; + }; + + let diff = now - last_received_time; + let max_rerequest_wait = self + .config + .reply_surbs + .maximum_reply_surb_rerequest_waiting_period; + let max_drop_wait = self + .config + .reply_surbs + .maximum_reply_surb_drop_waiting_period; + let max_rerequests = self.config.reply_surbs.maximum_reply_surbs_rerequests; + + // if we have already requested extra surbs because of the stale entry, + // don't do it again (otherwise we'll get stuck in a constant cycle of requesting more surbs + // if client is offline) + if vals.current_clear_rerequest_counter > max_rerequests { + to_remove.push(*pending_reply_target); + debug!("we have reached the maximum threshold of attempting to request surbs from {pending_reply_target}. dropping the sender"); + continue; + } + + if diff > max_rerequest_wait { + if diff > max_drop_wait { + to_remove.push(*pending_reply_target) + } else { + debug!("We haven't received any surbs in {} from {pending_reply_target}. Going to explicitly ask for more", humantime::format_duration(diff.unsigned_abs())); + vals.increment_current_clear_rerequest_counter(); + to_request.push(*pending_reply_target); + } + } + } + + for pending_reply_target in to_request { + self.request_reply_surbs_for_queue_clearing(pending_reply_target) + .await; + self.surbs_storage + .reset_pending_reception(&pending_reply_target) + } + for to_remove in to_remove { + // TODO: in the 'old' version we just removed pending messages, + // not retransmissions, but I think those should follow the same logic. + // if something breaks because of that. I guess here is your explanation, future reader + self.surb_senders.remove(&to_remove); + } + } + + pub(crate) async fn check_surb_refresh(&mut self) { + let Some(current_rotation_id) = self.topology_access.current_key_rotation_id().await else { + warn!("failed to retrieve current key rotation id from the network topology"); + return; + }; + + if let SurbRefreshState::WaitingForNextRotation { last_known } = self.surb_refresh_state { + if last_known == current_rotation_id { + trace!("no changes in key rotation id"); + } else { + // key rotation actually changed and given the polling rate (1/8th epoch) we should have plenty + // of time to perform the upgrade. + // but wait for one more call before doing this so that the clients could also resync + // their topologies and discover new rotation + self.surb_refresh_state = SurbRefreshState::ScheduledForNextInvocation; + } + return; + } + + // here we are in `SurbRefreshState::ScheduledForNextInvocation` state + + let mut marked_as_stale = HashMap::new(); + + // 1. mark all existing surbs we have as possibly stale + for mut map_entry in self.surbs_storage.as_raw_iter_mut() { + let (sender, received) = map_entry.pair_mut(); + let num_downgraded = received.downgrade_freshness(); + trace!("{sender}: {num_downgraded} downgraded"); + if num_downgraded != 0 { + marked_as_stale.insert(*sender, num_downgraded); + } + } + + // 2. attempt to re-request the equivalent number of fresh surbs + // TODO PROBLEM: if our request gets lost, we might be in trouble... + // we need some sort of retry mechanism + for (sender, num_to_request) in marked_as_stale { + if self + .request_additional_reply_surbs(sender, num_to_request as u32) + .await + .is_err() + { + warn!("surb refresh request failed") + } + } + + self.surb_refresh_state = SurbRefreshState::WaitingForNextRotation { + last_known: current_rotation_id, + }; + } + + pub(crate) async fn inspect_and_clear_stale_data(&mut self, now: OffsetDateTime) { + // technically we don't know if epoch is stuck, but we're flying in blind here, + // so we have to assume the worst and not purge anything depending on proper epoch progression + let is_epoch_stuck = self + .current_topology_metadata() + .await + .map(|m| self.config.key_rotation.epoch_stuck(m)) + .unwrap_or(false); + + // expected time of when the CURRENT key rotation has begun + let expected_current_key_rotation_start = self + .config + .key_rotation + .expected_current_key_rotation_start(now); + + // expected ID of the CURRENT key rotation + let expected_current_key_rotation = self + .config + .key_rotation + .expected_current_key_rotation_id(now); + + // time of the start of one epoch BEFORE the CURRENT rotation has begun + // this indicates the starting time of when packets with the current keys might have been constructed + let prior_epoch_start = + expected_current_key_rotation_start - self.config.key_rotation.epoch_duration; + + // time of the start of one epoch AFTER the current rotation has begun + // this indicates the end of transition period and any packets constructed with keys different + // from the current one are definitely invalid + let following_epoch_start = + expected_current_key_rotation_start + self.config.key_rotation.epoch_duration; + + // define a closure for validating individual surbs + // (we have to run it twice for different piles) + let basic_surb_retention_logic = |received_surb: &ReceivedReplySurb| { + if is_epoch_stuck { + let diff = now - received_surb.received_at(); + return diff < self.config.key_rotation.rotation_lifetime(); + } + + if received_surb.received_at() < prior_epoch_start { + // it's definitely from previous rotation + return false; + } + let surb_rotation = received_surb.key_rotation(); + + if surb_rotation.is_unknown() { + // can't do anything, so just retain it + return true; + } + + // TODO: will this backfire during transition period where we need surbs to refresh surbs + // and we failed to send a request? + if surb_rotation.is_even() && expected_current_key_rotation % 2 == 1 { + return false; + } + + if surb_rotation.is_odd() && expected_current_key_rotation % 2 == 0 { + return false; + } + + true + }; + + // 1. purge full old clients data (this applies to RECEIVER) + self.surbs_storage.retain(|_, received| { + if is_epoch_stuck { + // if epoch is stuck, we can't do much (because we don't know for certain if rotation has advanced) + // apart from the basic check of surbs being received more than maximum lifetime of a rotation + // because at that point we know they must be invalid + let diff = now - received.surbs_last_received_at(); + return diff < self.config.key_rotation.rotation_lifetime(); + } + + // if surbs were received more than 1h before the start of the current rotation, + // they're DEFINITELY invalid. + // if it was up until 1h AFTER the start of the current rotation they MIGHT be valid - + // we don't know for sure, unless the client explicitly attached rotation information + // (which only applies to more recent versions of clients so we can't 100% rely on that) + if received.surbs_last_received_at() < prior_epoch_start { + return false; + } + + // 1.1. check individual surbs (same basic logic applies) + received.retain_fresh_surbs(&basic_surb_retention_logic); + + // 1.2. check the possibly stale entries + // 1.2.1. check if we're beyond the key rotation transition period, + // if so those surbs are definitely unusable + if now > following_epoch_start { + received.drop_possibly_stale_surbs(); + } + + // 1.2.2. otherwise continue with the same logic as the fresh ones + received.retain_possibly_stale_surbs(&basic_surb_retention_logic); + + // no surbs left, we're not expecting any AND we haven't received anything in a while + // (i.e. sender probably abandoned us) + let max_drop_wait = self + .config + .reply_surbs + .maximum_reply_surb_drop_waiting_period; + let last_received = received.surbs_last_received_at(); + + let possibly_abandoned = last_received + max_drop_wait < now; + if received.is_empty() && received.pending_reception() == 0 && possibly_abandoned { + return false; + } + + true + }); + + // 1.3 inspect old unavailable receivers to clear any stale data + self.unavailable + .retain(|_, last_reported| now - *last_reported < time::Duration::seconds(30)); + } +} diff --git a/common/client-core/src/client/replies/reply_controller/requests.rs b/common/client-core/src/client/replies/reply_controller/requests.rs index 2625034710..d4a7014065 100644 --- a/common/client-core/src/client/replies/reply_controller/requests.rs +++ b/common/client-core/src/client/replies/reply_controller/requests.rs @@ -3,12 +3,12 @@ use crate::client::real_messages_control::acknowledgement_control::PendingAcknowledgement; use futures::channel::{mpsc, oneshot}; -use log::error; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::anonymous_replies::ReplySurbWithKeyRotation; use nym_task::connections::{ConnectionId, TransmissionLane}; use std::sync::Weak; +use tracing::error; pub(crate) fn new_control_channels() -> (ReplyControllerSender, ReplyControllerReceiver) { let (tx, rx) = mpsc::unbounded(); diff --git a/common/client-core/src/client/replies/reply_controller/sender_controller.rs b/common/client-core/src/client/replies/reply_controller/sender_controller.rs new file mode 100644 index 0000000000..4d5aac999b --- /dev/null +++ b/common/client-core/src/client/replies/reply_controller/sender_controller.rs @@ -0,0 +1,101 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::real_messages_control::message_handler::MessageHandler; +use crate::client::replies::reply_controller::Config; +use nym_client_core_surb_storage::{CombinedReplyStorage, SentReplyKeys, UsedSenderTags}; +use nym_crypto::aes::cipher::crypto_common::rand_core::CryptoRng; +use nym_sphinx::addressing::Recipient; +use rand::Rng; +use std::cmp::min; +use std::time::Duration; +use time::OffsetDateTime; +use tracing::{debug, trace, warn}; + +/// Reply controller responsible for controlling sender-related part +/// of replies, such as checking if any reply keys are stale +pub struct SenderReplyController { + config: Config, + + tags_storage: UsedSenderTags, + sent_reply_keys: SentReplyKeys, + message_handler: MessageHandler, +} + +impl SenderReplyController +where + R: CryptoRng + Rng, +{ + pub(crate) fn new( + config: Config, + storage: &CombinedReplyStorage, + message_handler: MessageHandler, + ) -> Self { + SenderReplyController { + config, + tags_storage: storage.tags_storage(), + sent_reply_keys: storage.key_storage(), + message_handler, + } + } + + pub(crate) async fn handle_surb_request(&mut self, recipient: Recipient, mut amount: u32) { + // 1. check whether we sent any surbs in the past to this recipient, otherwise + // they have no business in asking for more + if !self.tags_storage.exists(&recipient) { + warn!("{recipient} asked us for reply SURBs even though we never sent them any anonymous messages before!"); + return; + } + + // 2. check whether the requested amount is within sane range + if amount + > self + .config + .reply_surbs + .maximum_allowed_reply_surb_request_size + { + warn!("The requested reply surb amount is larger than our maximum allowed ({amount} > {}). Lowering it to a more sane value...", self.config.reply_surbs.maximum_allowed_reply_surb_request_size); + amount = self + .config + .reply_surbs + .maximum_allowed_reply_surb_request_size; + } + + // 3. construct and send the surbs away + // (send them in smaller batches to make the experience a bit smoother + let mut remaining = amount; + while remaining > 0 { + let to_send = min(remaining, 100); + if let Err(err) = self + .message_handler + .try_send_additional_reply_surbs( + recipient, + to_send, + nym_sphinx::params::PacketType::Mix, + ) + .await + { + warn!("failed to send additional surbs to {recipient} - {err}"); + } else { + trace!("sent {to_send} reply SURBs to {recipient}"); + } + + remaining -= to_send; + } + } + + pub(crate) fn inspect_and_clear_stale_data(&self, now: OffsetDateTime) { + // check reply keys (this applies to SENDER) + self.sent_reply_keys.retain(|_, reply_key| { + let diff = now - reply_key.sent_at; + if diff > self.config.reply_surbs.maximum_reply_key_age { + let std_diff = Duration::try_from(diff).unwrap_or_default(); + let diff_formatted = humantime::format_duration(std_diff); + debug!("it's been {diff_formatted} since we created this reply key. it's probably never going to get used, so we're going to purge it..."); + false + } else { + true + } + }); + } +} diff --git a/common/client-core/src/client/statistics_control.rs b/common/client-core/src/client/statistics_control.rs index 01f4d25f1b..1bf28c9f5b 100644 --- a/common/client-core/src/client/statistics_control.rs +++ b/common/client-core/src/client/statistics_control.rs @@ -93,14 +93,14 @@ impl StatisticsControl { None, ); if let Err(err) = self.report_tx.send(report_message).await { - log::error!("Failed to report client stats: {:?}", err); + tracing::error!("Failed to report client stats: {:?}", err); } else { self.stats.reset(); } } async fn run(&mut self) { - log::debug!("Started StatisticsControl with graceful shutdown support"); + tracing::debug!("Started StatisticsControl with graceful shutdown support"); #[cfg(not(target_arch = "wasm32"))] let mut stats_report_interval = tokio_stream::wrappers::IntervalStream::new( @@ -133,13 +133,13 @@ impl StatisticsControl { tokio::select! { biased; _ = self.task_client.recv() => { - log::trace!("StatisticsControl: Received shutdown"); + tracing::trace!("StatisticsControl: Received shutdown"); break; }, stats_event = self.stats_rx.recv() => match stats_event { Some(stats_event) => self.stats.handle_event(stats_event), None => { - log::trace!("StatisticsControl: shutting down due to closed stats channel"); + tracing::trace!("StatisticsControl: shutting down due to closed stats channel"); break; } }, @@ -161,7 +161,7 @@ impl StatisticsControl { } } } - log::debug!("StatisticsControl: Exiting"); + tracing::debug!("StatisticsControl: Exiting"); } pub(crate) fn start(mut self) { diff --git a/common/client-core/src/client/topology_control/accessor.rs b/common/client-core/src/client/topology_control/accessor.rs index b1f74c4f24..5c08a6a9ae 100644 --- a/common/client-core/src/client/topology_control/accessor.rs +++ b/common/client-core/src/client/topology_control/accessor.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use nym_sphinx::addressing::clients::Recipient; -use nym_topology::{NymRouteProvider, NymTopology, NymTopologyError}; +use nym_topology::{NymRouteProvider, NymTopology, NymTopologyError, NymTopologyMetadata}; +use nym_validator_client::models::KeyRotationId; use std::ops::Deref; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -134,6 +135,21 @@ impl TopologyAccessor { } } + pub async fn current_mixnet_epoch_id(&self) -> Option { + let route_provider = self.current_route_provider().await?; + Some(route_provider.absolute_epoch_id()) + } + + pub async fn current_key_rotation_id(&self) -> Option { + let route_provider = self.current_route_provider().await?; + Some(route_provider.current_key_rotation()) + } + + pub async fn current_metadata(&self) -> Option { + let route_provider = self.current_route_provider().await?; + Some(route_provider.metadata()) + } + pub async fn manually_change_topology(&self, new_topology: NymTopology) { self.inner.controlled_manually.store(true, Ordering::SeqCst); self.inner.update(Some(new_topology)).await; diff --git a/common/client-core/src/client/topology_control/mod.rs b/common/client-core/src/client/topology_control/mod.rs index b50623c992..1aaff25120 100644 --- a/common/client-core/src/client/topology_control/mod.rs +++ b/common/client-core/src/client/topology_control/mod.rs @@ -4,11 +4,11 @@ use crate::spawn_future; pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit}; use futures::StreamExt; -use log::*; use nym_sphinx::addressing::nodes::NodeIdentity; use nym_task::TaskClient; use nym_topology::NymTopologyError; use std::time::Duration; +use tracing::*; #[cfg(not(target_arch = "wasm32"))] use tokio::time::sleep; @@ -20,7 +20,7 @@ mod accessor; pub mod nym_api_provider; pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider}; -pub use nym_topology::provider_trait::TopologyProvider; +pub use nym_topology::provider_trait::{ToTopologyMetadata, TopologyProvider}; // TODO: move it to config later const MAX_FAILURE_COUNT: usize = 10; @@ -169,12 +169,12 @@ impl TopologyRefresher { self.try_refresh().await; }, _ = self.task_client.recv() => { - log::trace!("TopologyRefresher: Received shutdown"); + tracing::trace!("TopologyRefresher: Received shutdown"); }, } } self.task_client.recv_timeout().await; - log::debug!("TopologyRefresher: Exiting"); + tracing::debug!("TopologyRefresher: Exiting"); }) } } diff --git a/common/client-core/src/client/topology_control/nym_api_provider.rs b/common/client-core/src/client/topology_control/nym_api_provider.rs index 3c060f36ea..339cd12653 100644 --- a/common/client-core/src/client/topology_control/nym_api_provider.rs +++ b/common/client-core/src/client/topology_control/nym_api_provider.rs @@ -2,13 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use log::{debug, error, warn}; -use nym_topology::provider_trait::TopologyProvider; -use nym_topology::{NymTopology, NymTopologyMetadata}; -use nym_validator_client::UserAgent; +use nym_topology::provider_trait::{ToTopologyMetadata, TopologyProvider}; +use nym_topology::NymTopology; use rand::prelude::SliceRandom; use rand::thread_rng; use std::cmp::min; +use tracing::{debug, error, warn}; use url::Url; #[derive(Debug)] @@ -49,18 +48,10 @@ impl NymApiTopologyProvider { pub fn new( config: impl Into, mut nym_api_urls: Vec, - user_agent: Option, + mut validator_client: nym_validator_client::client::NymApiClient, ) -> Self { nym_api_urls.shuffle(&mut thread_rng()); - - let validator_client = if let Some(user_agent) = user_agent { - nym_validator_client::client::NymApiClient::new_with_user_agent( - nym_api_urls[0].clone(), - user_agent, - ) - } else { - nym_validator_client::client::NymApiClient::new(nym_api_urls[0].clone()) - }; + validator_client.change_nym_api(nym_api_urls[0].clone()); NymApiTopologyProvider { config: config.into(), @@ -108,12 +99,8 @@ impl NymApiTopologyProvider { .filter(|n| n.performance.round_to_integer() >= self.config.min_node_performance()) .collect::>(); - NymTopology::new( - NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id), - rewarded_set, - Vec::new(), - ) - .with_skimmed_nodes(&nodes_filtered) + NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new()) + .with_skimmed_nodes(&nodes_filtered) } else { // if we're not using extended topology, we're only getting active set mixnodes and gateways @@ -136,7 +123,7 @@ impl NymApiTopologyProvider { let metadata = mixnodes_res.metadata; let mixnodes = mixnodes_res.nodes; - if gateways_res.metadata != metadata { + if !gateways_res.metadata.consistency_check(&metadata) { warn!("inconsistent nodes metadata between mixnodes and gateways calls! {metadata:?} and {:?}", gateways_res.metadata); return None; } @@ -161,12 +148,8 @@ impl NymApiTopologyProvider { } } - NymTopology::new( - NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id), - rewarded_set, - Vec::new(), - ) - .with_skimmed_nodes(&nodes) + NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new()) + .with_skimmed_nodes(&nodes) }; if !topology.is_minimally_routable() { diff --git a/common/client-core/src/client/transmission_buffer.rs b/common/client-core/src/client/transmission_buffer.rs index e6eb7d6891..b0f8f8092e 100644 --- a/common/client-core/src/client/transmission_buffer.rs +++ b/common/client-core/src/client/transmission_buffer.rs @@ -36,11 +36,18 @@ impl SizedData for Fragment { } } -#[derive(Default)] pub(crate) struct TransmissionBuffer { buffer: HashMap>, } +impl Default for TransmissionBuffer { + fn default() -> Self { + TransmissionBuffer { + buffer: HashMap::new(), + } + } +} + impl TransmissionBuffer { pub(crate) fn new() -> Self { TransmissionBuffer { @@ -211,7 +218,7 @@ impl TransmissionBuffer { }; let msg = self.pop_front_from_lane(&lane)?; - log::trace!("picking to send from lane: {:?}", lane); + tracing::trace!("picking to send from lane: {:?}", lane); Some((lane, msg)) } diff --git a/common/client-core/src/error.rs b/common/client-core/src/error.rs index be00e4637f..a97030a24e 100644 --- a/common/client-core/src/error.rs +++ b/common/client-core/src/error.rs @@ -6,6 +6,7 @@ use nym_crypto::asymmetric::ed25519::Ed25519RecoveryError; use nym_gateway_client::error::GatewayClientError; use nym_topology::node::RoutingNodeError; use nym_topology::{NodeId, NymTopologyError}; +use nym_validator_client::nym_api::error::NymAPIError; use nym_validator_client::ValidatorClientError; use std::error::Error; use std::path::PathBuf; @@ -52,7 +53,15 @@ pub enum ClientCoreError { #[error("list of nym apis is empty")] ListOfNymApisIsEmpty, - #[error("the current network topology seem to be insufficient to route any packets through")] + #[error("failed to resolve a query to nym API: {source}")] + NymApiQueryFailure { + #[from] + source: NymAPIError, + }, + + #[error( + "the current network topology seem to be insufficient to route any packets through:\n\t{0}" + )] InsufficientNetworkTopology(#[from] NymTopologyError), #[error("experienced a failure with our reply surb persistent storage: {source}")] diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index da92fe093a..0d4a9fa742 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -4,7 +4,6 @@ use crate::error::ClientCoreError; use crate::init::types::RegistrationResult; use futures::{SinkExt, StreamExt}; -use log::{debug, info, trace, warn}; use nym_crypto::asymmetric::ed25519; use nym_gateway_client::GatewayClient; use nym_topology::node::RoutingNode; @@ -14,6 +13,7 @@ use rand::{seq::SliceRandom, Rng}; #[cfg(unix)] use std::os::fd::RawFd; use std::{sync::Arc, time::Duration}; +use tracing::{debug, info, trace, warn}; use tungstenite::Message; use url::Url; @@ -105,7 +105,7 @@ pub async fn gateways_for_init( nym_validator_client::client::NymApiClient::new(nym_api.clone()) }; - log::debug!("Fetching list of gateways from: {nym_api}"); + tracing::debug!("Fetching list of gateways from: {nym_api}"); let gateways = client .get_all_basic_entry_assigned_nodes_with_metadata() @@ -113,7 +113,7 @@ pub async fn gateways_for_init( .nodes; info!("nym api reports {} gateways", gateways.len()); - log::trace!("Gateways: {:#?}", gateways); + tracing::trace!("Gateways: {:#?}", gateways); // filter out gateways below minimum performance and ones that could operate as a mixnode // (we don't want instability) @@ -123,10 +123,10 @@ pub async fn gateways_for_init( .filter(|g| g.performance.round_to_integer() >= minimum_performance) .filter_map(|gateway| gateway.try_into().ok()) .collect::>(); - log::debug!("After checking validity: {}", valid_gateways.len()); - log::trace!("Valid gateways: {:#?}", valid_gateways); + tracing::debug!("After checking validity: {}", valid_gateways.len()); + tracing::trace!("Valid gateways: {:#?}", valid_gateways); - log::info!( + tracing::info!( "and {} after validity and performance filtering", valid_gateways.len() ); @@ -289,7 +289,7 @@ pub(super) fn get_specified_gateway( gateways: &[RoutingNode], must_use_tls: bool, ) -> Result { - log::debug!("Requesting specified gateway: {}", gateway_identity); + tracing::debug!("Requesting specified gateway: {}", gateway_identity); let user_gateway = ed25519::PublicKey::from_base58_string(gateway_identity) .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; @@ -329,7 +329,7 @@ pub(super) async fn register_with_gateway( ); gateway_client.establish_connection().await.map_err(|err| { - log::warn!("Failed to establish connection with gateway!"); + tracing::warn!("Failed to establish connection with gateway!"); ClientCoreError::GatewayClientError { gateway_id: gateway_id.to_base58_string(), source: Box::new(err), @@ -339,7 +339,7 @@ pub(super) async fn register_with_gateway( .perform_initial_authentication() .await .map_err(|err| { - log::warn!("Failed to register with the gateway {gateway_id}: {err}"); + tracing::warn!("Failed to register with the gateway {gateway_id}: {err}"); ClientCoreError::GatewayClientError { gateway_id: gateway_id.to_base58_string(), source: Box::new(err), diff --git a/common/client-core/src/init/mod.rs b/common/client-core/src/init/mod.rs index a83d7962bf..5bece573fd 100644 --- a/common/client-core/src/init/mod.rs +++ b/common/client-core/src/init/mod.rs @@ -63,7 +63,7 @@ where K::StorageError: Send + Sync + 'static, D::StorageError: Send + Sync + 'static, { - log::trace!("Setting up new gateway"); + tracing::trace!("Setting up new gateway"); // if we're setting up new gateway, we must have had generated long-term client keys before let client_keys = load_client_keys(key_store).await?; @@ -202,10 +202,10 @@ where K::StorageError: Send + Sync + 'static, D::StorageError: Send + Sync + 'static, { - log::debug!("Setting up gateway"); + tracing::debug!("Setting up gateway"); match setup { GatewaySetup::MustLoad { gateway_id } => { - log::debug!("GatewaySetup::MustLoad with id: {gateway_id:?}"); + tracing::debug!("GatewaySetup::MustLoad with id: {gateway_id:?}"); use_loaded_gateway_details(key_store, details_store, gateway_id).await } GatewaySetup::New { @@ -214,7 +214,7 @@ where #[cfg(unix)] connection_fd_callback, } => { - log::debug!("GatewaySetup::New with spec: {specification:?}"); + tracing::debug!("GatewaySetup::New with spec: {specification:?}"); setup_new_gateway( key_store, details_store, @@ -230,7 +230,7 @@ where gateway_details, client_keys: managed_keys, } => { - log::debug!("GatewaySetup::ReuseConnection"); + tracing::debug!("GatewaySetup::ReuseConnection"); Ok(reuse_gateway_connection( *authenticated_ephemeral_client, *gateway_details, diff --git a/common/client-core/surb-storage/Cargo.toml b/common/client-core/surb-storage/Cargo.toml index 7a902bef94..45f0532a85 100644 --- a/common/client-core/surb-storage/Cargo.toml +++ b/common/client-core/surb-storage/Cargo.toml @@ -9,7 +9,7 @@ license.workspace = true [dependencies] async-trait.workspace = true dashmap.workspace = true -log.workspace = true +tracing.workspace = true thiserror.workspace = true time.workspace = true @@ -20,7 +20,7 @@ nym-task = { path = "../../task" } [target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx] workspace = true -features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] +features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"] optional = true [build-dependencies] diff --git a/common/client-core/surb-storage/fs_surbs_migrations/20250627120000_types_cleanup.sql b/common/client-core/surb-storage/fs_surbs_migrations/20250627120000_types_cleanup.sql new file mode 100644 index 0000000000..f0bf246cc1 --- /dev/null +++ b/common/client-core/surb-storage/fs_surbs_migrations/20250627120000_types_cleanup.sql @@ -0,0 +1,81 @@ +/* + * Copyright 2025 - Nym Technologies SA + * SPDX-License-Identifier: Apache-2.0 + */ + +-- change `previous_flush_timestamp` unix timestamp to `previous_flush` timestamp +CREATE TABLE status_new +( + flush_in_progress INTEGER NOT NULL, + previous_flush TIMESTAMP WITHOUT TIME ZONE NOT NULL, + client_in_use INTEGER NOT NULL +); + +INSERT INTO status_new (flush_in_progress, previous_flush, client_in_use) +SELECT flush_in_progress, + datetime(previous_flush_timestamp, 'unixepoch') AS previous_flush, + client_in_use +FROM status; + +DROP TABLE status; +ALTER TABLE status_new + RENAME TO status; + + +-- change `sent_at_timestamp` unix timestamp to `sent_at` timestamp +CREATE TABLE reply_key_new +( + key_digest BLOB NOT NULL UNIQUE, + reply_key BLOB NOT NULL UNIQUE, + sent_at TIMESTAMP WITHOUT TIME ZONE NOT NULL +); + +INSERT INTO reply_key_new (key_digest, reply_key, sent_at) +SELECT key_digest, + reply_key, + datetime(sent_at_timestamp, 'unixepoch') AS sent_at +FROM reply_key; + +DROP TABLE reply_key; +ALTER TABLE reply_key_new + RENAME TO reply_key; + + +-- change `last_sent_timestamp` unix timestamp to `sent_at` last_sent +CREATE TABLE reply_surb_sender_new +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + last_sent TIMESTAMP WITHOUT TIME ZONE NOT NULL, + tag BLOB NOT NULL UNIQUE +); + +INSERT INTO reply_surb_sender_new (id, last_sent, tag) +SELECT id, + datetime(last_sent_timestamp, 'unixepoch') AS last_sent, + tag +FROM reply_surb_sender; + + +-- recreate `reply_surb` table due to foreign key constraint +CREATE TABLE reply_surb_new +( + reply_surb_sender_id INTEGER NOT NULL, + reply_surb BLOB NOT NULL, + encoded_key_rotation TINYINT NOT NULL, + + FOREIGN KEY (reply_surb_sender_id) REFERENCES reply_surb_sender_new (id) +); + +INSERT INTO reply_surb_new +SELECT * +FROM reply_surb; + +DROP TABLE reply_surb; +ALTER TABLE reply_surb_new + RENAME TO reply_surb; + +DROP TABLE reply_surb_sender; +ALTER TABLE reply_surb_sender_new + RENAME TO reply_surb_sender; + + diff --git a/common/client-core/surb-storage/fs_surbs_migrations/20250702120000_remove_sender_tag.sql b/common/client-core/surb-storage/fs_surbs_migrations/20250702120000_remove_sender_tag.sql new file mode 100644 index 0000000000..2b44a49a2e --- /dev/null +++ b/common/client-core/surb-storage/fs_surbs_migrations/20250702120000_remove_sender_tag.sql @@ -0,0 +1,12 @@ +/* + * Copyright 2025 - Nym Technologies SA + * SPDX-License-Identifier: Apache-2.0 + */ + +-- don't persist sender_tag in the DB. instead generate fresh one on each restart +-- this will: +-- A) further help against correlation attacks +-- B) realistically after client restarts, we might be in new key rotation anyway meaning receiver would have to start +-- "from scratch" with surbs + +DROP TABLE sender_tag; \ No newline at end of file diff --git a/common/client-core/surb-storage/src/backend/browser_backend.rs b/common/client-core/surb-storage/src/backend/browser_backend.rs index de39fd49b6..3be6eb4aa2 100644 --- a/common/client-core/surb-storage/src/backend/browser_backend.rs +++ b/common/client-core/surb-storage/src/backend/browser_backend.rs @@ -4,6 +4,7 @@ use crate::backend::Empty; use crate::{CombinedReplyStorage, ReplyStorageBackend}; use async_trait::async_trait; +use time::OffsetDateTime; // well, right now we don't have the browser storage : ( // so we keep everything in memory @@ -38,7 +39,10 @@ impl ReplyStorageBackend for Backend { self.empty.init_fresh(fresh).await } - async fn load_surb_storage(&self) -> Result { - self.empty.load_surb_storage().await + async fn load_surb_storage( + &self, + surb_freshness_cutoff: OffsetDateTime, + ) -> Result { + self.empty.load_surb_storage(surb_freshness_cutoff).await } } diff --git a/common/client-core/surb-storage/src/backend/fs_backend/manager.rs b/common/client-core/surb-storage/src/backend/fs_backend/manager.rs index c8f39fdf96..87ff19b78d 100644 --- a/common/client-core/surb-storage/src/backend/fs_backend/manager.rs +++ b/common/client-core/surb-storage/src/backend/fs_backend/manager.rs @@ -3,17 +3,15 @@ use crate::backend::fs_backend::{ error::StorageError, - models::{ - ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, - StoredSurbSender, - }, + models::{ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSurbSender}, }; -use log::{error, info}; use sqlx::{ sqlite::{SqliteAutoVacuum, SqliteSynchronous}, ConnectOptions, }; use std::path::Path; +use time::OffsetDateTime; +use tracing::{error, info}; #[derive(Debug, Clone)] pub struct StorageManager { @@ -70,9 +68,11 @@ impl StorageManager { } pub async fn create_status_table(&self) -> Result<(), sqlx::Error> { - sqlx::query!("INSERT INTO status(flush_in_progress, previous_flush_timestamp, client_in_use) VALUES (0, 0, 1)") - .execute(&self.connection_pool) - .await?; + sqlx::query!( + "INSERT INTO status(flush_in_progress, previous_flush, client_in_use) VALUES (0, 0, 1)" + ) + .execute(&self.connection_pool) + .await?; Ok(()) } @@ -83,18 +83,18 @@ impl StorageManager { .map(|r| r.flush_in_progress > 0) } - pub async fn set_previous_flush_timestamp(&self, timestamp: i64) -> Result<(), sqlx::Error> { - sqlx::query!("UPDATE status SET previous_flush_timestamp = ?", timestamp) + pub async fn set_previous_flush(&self, timestamp: OffsetDateTime) -> Result<(), sqlx::Error> { + sqlx::query!("UPDATE status SET previous_flush = ?", timestamp) .execute(&self.connection_pool) .await?; Ok(()) } - pub async fn get_previous_flush_timestamp(&self) -> Result { - sqlx::query!("SELECT previous_flush_timestamp FROM status;") + pub async fn get_previous_flush_time(&self) -> Result { + sqlx::query!(r#"SELECT previous_flush AS "previous_flush: OffsetDateTime" FROM status"#) .fetch_one(&self.connection_pool) .await - .map(|r| r.previous_flush_timestamp) + .map(|r| r.previous_flush) } pub async fn set_flush_status(&self, in_progress: bool) -> Result<(), sqlx::Error> { @@ -120,32 +120,6 @@ impl StorageManager { Ok(()) } - pub async fn delete_all_tags(&self) -> Result<(), sqlx::Error> { - sqlx::query!("DELETE FROM sender_tag;") - .execute(&self.connection_pool) - .await?; - Ok(()) - } - - pub async fn get_tags(&self) -> Result, sqlx::Error> { - sqlx::query_as!(StoredSenderTag, "SELECT * FROM sender_tag;",) - .fetch_all(&self.connection_pool) - .await - } - - pub async fn insert_tag(&self, stored_tag: StoredSenderTag) -> Result<(), sqlx::Error> { - sqlx::query!( - r#" - INSERT INTO sender_tag(recipient, tag) VALUES (?, ?); - "#, - stored_tag.recipient, - stored_tag.tag - ) - .execute(&self.connection_pool) - .await?; - Ok(()) - } - pub async fn delete_all_reply_keys(&self) -> Result<(), sqlx::Error> { sqlx::query!("DELETE FROM reply_key;") .execute(&self.connection_pool) @@ -154,7 +128,7 @@ impl StorageManager { } pub async fn get_reply_keys(&self) -> Result, sqlx::Error> { - sqlx::query_as!(StoredReplyKey, "SELECT * FROM reply_key;",) + sqlx::query_as("SELECT * FROM reply_key;") .fetch_all(&self.connection_pool) .await } @@ -165,11 +139,11 @@ impl StorageManager { ) -> Result<(), sqlx::Error> { sqlx::query!( r#" - INSERT INTO reply_key(key_digest, reply_key, sent_at_timestamp) VALUES (?, ?, ?); + INSERT INTO reply_key(key_digest, reply_key, sent_at) VALUES (?, ?, ?); "#, stored_reply_key.key_digest, stored_reply_key.reply_key, - stored_reply_key.sent_at_timestamp + stored_reply_key.sent_at ) .execute(&self.connection_pool) .await?; @@ -177,7 +151,7 @@ impl StorageManager { } pub async fn get_surb_senders(&self) -> Result, sqlx::Error> { - sqlx::query_as!(StoredSurbSender, "SELECT * FROM reply_surb_sender;",) + sqlx::query_as("SELECT * FROM reply_surb_sender;") .fetch_all(&self.connection_pool) .await } @@ -188,10 +162,10 @@ impl StorageManager { ) -> Result { let id = sqlx::query!( r#" - INSERT INTO reply_surb_sender(tag, last_sent_timestamp) VALUES (?, ?); + INSERT INTO reply_surb_sender(tag, last_sent) VALUES (?, ?); "#, stored_surb_sender.tag, - stored_surb_sender.last_sent_timestamp + stored_surb_sender.last_sent ) .execute(&self.connection_pool) .await? @@ -206,7 +180,7 @@ impl StorageManager { sqlx::query_as!( StoredReplySurb, r#" - SELECT reply_surb_sender_id, reply_surb, encoded_key_rotation as "encoded_key_rotation: u8" FROM reply_surb + SELECT reply_surb_sender_id, reply_surb, encoded_key_rotation as "encoded_key_rotation: u8" FROM reply_surb WHERE reply_surb_sender_id = ? "#, sender_id diff --git a/common/client-core/surb-storage/src/backend/fs_backend/mod.rs b/common/client-core/surb-storage/src/backend/fs_backend/mod.rs index 4467e17538..9a2621f728 100644 --- a/common/client-core/surb-storage/src/backend/fs_backend/mod.rs +++ b/common/client-core/surb-storage/src/backend/fs_backend/mod.rs @@ -3,18 +3,16 @@ use crate::backend::fs_backend::manager::StorageManager; use crate::backend::fs_backend::models::{ - ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender, + ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSurbSender, }; use crate::surb_storage::ReceivedReplySurbs; -use crate::{ - CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys, UsedSenderTags, -}; +use crate::{CombinedReplyStorage, ReceivedReplySurbsMap, ReplyStorageBackend, SentReplyKeys}; use async_trait::async_trait; -use log::{debug, error, info, warn}; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use std::fs; use std::path::{Path, PathBuf}; use time::OffsetDateTime; +use tracing::{error, info, warn}; pub use self::error::StorageError; @@ -52,10 +50,7 @@ impl Backend { Ok(backend) } - pub async fn try_load>( - database_path: P, - fresh_sender_tags: bool, - ) -> Result { + pub async fn try_load>(database_path: P) -> Result { let owned_path: PathBuf = database_path.as_ref().into(); if owned_path.file_name().is_none() { return Err(StorageError::DatabasePathWithoutFilename { @@ -71,8 +66,8 @@ impl Backend { return Err(StorageError::IncompleteDataFlush); } - let last_flush_timestamp = manager.get_previous_flush_timestamp().await?; - if last_flush_timestamp == 0 { + let last_flush = manager.get_previous_flush_time().await?; + if last_flush == OffsetDateTime::UNIX_EPOCH { // either this client has been running since 1970 or the flush failed return Err(StorageError::IncompleteDataFlush); } @@ -92,15 +87,6 @@ impl Backend { return Err(err.into()); } - let last_flush = match OffsetDateTime::from_unix_timestamp(last_flush_timestamp) { - Ok(last_flush) => last_flush, - Err(err) => { - return Err(StorageError::CorruptedData { - details: format!("failed to parse stored timestamp - {err}"), - }); - } - }; - // in theory clients can use our reply surbs whenever they want, even a year in the future // (assuming no key rotation has happened) // but the way it's currently coded, everyone will purge old data @@ -118,14 +104,6 @@ impl Backend { manager.delete_all_reply_keys().await?; } - if days > 2 { - info!("it's been over {days} days and {hours} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now."); - manager.delete_all_tags().await?; - } else if fresh_sender_tags { - debug!("starting with fresh sender tags"); - manager.delete_all_tags().await?; - } - Ok(Backend { temporary_old_path: None, database_path: owned_path, @@ -177,7 +155,7 @@ impl Backend { async fn end_storage_flush(&self) -> Result<(), StorageError> { self.manager - .set_previous_flush_timestamp(OffsetDateTime::now_utc().unix_timestamp()) + .set_previous_flush(OffsetDateTime::now_utc()) .await?; Ok(self.manager.set_flush_status(false).await?) } @@ -190,29 +168,6 @@ impl Backend { Ok(self.manager.set_client_in_use_status(false).await?) } - async fn get_stored_tags(&self) -> Result { - let stored = self.manager.get_tags().await?; - - // stop at the first instance of corruption. if even a single entry is malformed, - // something weird has happened and we can't trust the rest of the data - let raw = stored - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - - Ok(UsedSenderTags::from_raw(raw)) - } - - async fn dump_sender_tags(&self, tags: &UsedSenderTags) -> Result<(), StorageError> { - for map_ref in tags.as_raw_iter() { - let (recipient, tag) = map_ref.pair(); - self.manager - .insert_tag(StoredSenderTag::new(*recipient, *tag)) - .await?; - } - Ok(()) - } - async fn get_stored_reply_keys(&self) -> Result { let stored = self.manager.get_reply_keys().await?; @@ -236,14 +191,17 @@ impl Backend { Ok(()) } - async fn get_stored_reply_surbs(&self) -> Result { + async fn get_stored_reply_surbs( + &self, + surb_freshness_cutoff: OffsetDateTime, + ) -> Result { let surb_senders = self.manager.get_surb_senders().await?; let metadata = self.get_reply_surb_storage_metadata().await?; let mut received_surbs = Vec::with_capacity(surb_senders.len()); for sender in surb_senders { let sender_id = sender.id; - let (sender_tag, surbs_last_received_at_timestamp): (AnonymousSenderTag, i64) = + let (sender_tag, surbs_last_received_at): (AnonymousSenderTag, OffsetDateTime) = sender.try_into()?; let stored_surbs = self .manager @@ -255,15 +213,17 @@ impl Backend { received_surbs.push(( sender_tag, - ReceivedReplySurbs::new_retrieved(stored_surbs, surbs_last_received_at_timestamp), + ReceivedReplySurbs::new_retrieved(stored_surbs, surbs_last_received_at), )) } - Ok(ReceivedReplySurbsMap::from_raw( + let received_surbs = ReceivedReplySurbsMap::from_raw( metadata.min_reply_surb_threshold as usize, metadata.max_reply_surb_threshold as usize, received_surbs, - )) + ); + received_surbs.drop_stale_loaded_surbs(surb_freshness_cutoff); + Ok(received_surbs) } async fn dump_reply_surbs( @@ -285,6 +245,14 @@ impl Backend { .insert_reply_surb(StoredReplySurb::new(sender_id, reply_surb)) .await? } + + // TODO: should we also retain the stale ones? + if received_surbs.possibly_stale_left() != 0 { + warn!( + "dropping {} possibly stale surbs for {tag}", + received_surbs.possibly_stale_left() + ); + } } Ok(()) } @@ -328,7 +296,6 @@ impl ReplyStorageBackend for Backend { self.rotate().await?; self.start_storage_flush().await?; - self.dump_sender_tags(storage.tags_storage_ref()).await?; self.dump_sender_reply_keys(storage.key_storage_ref()) .await?; let surbs_ref = storage.surbs_storage_ref(); @@ -345,12 +312,14 @@ impl ReplyStorageBackend for Backend { .await } - async fn load_surb_storage(&self) -> Result { + async fn load_surb_storage( + &self, + surb_freshness_cutoff: OffsetDateTime, + ) -> Result { let reply_keys = self.get_stored_reply_keys().await?; - let tags = self.get_stored_tags().await?; - let reply_surbs = self.get_stored_reply_surbs().await?; + let reply_surbs = self.get_stored_reply_surbs(surb_freshness_cutoff).await?; - Ok(CombinedReplyStorage::load(reply_keys, reply_surbs, tags)) + Ok(CombinedReplyStorage::load(reply_keys, reply_surbs)) } async fn stop_storage_session(self) -> Result<(), Self::StorageError> { diff --git a/common/client-core/surb-storage/src/backend/fs_backend/models.rs b/common/client-core/surb-storage/src/backend/fs_backend/models.rs index c91c14b591..3c3ad9da3b 100644 --- a/common/client-core/surb-storage/src/backend/fs_backend/models.rs +++ b/common/client-core/surb-storage/src/backend/fs_backend/models.rs @@ -3,6 +3,7 @@ use crate::backend::fs_backend::error::StorageError; use crate::key_storage::UsedReplyKey; +use crate::ReceivedReplySurb; use nym_crypto::generic_array::typenum::Unsigned; use nym_crypto::Digest; use nym_sphinx::addressing::clients::{Recipient, RecipientBytes}; @@ -12,6 +13,8 @@ use nym_sphinx::anonymous_replies::{ ReplySurb, ReplySurbWithKeyRotation, SurbEncryptionKey, SurbEncryptionKeySize, }; use nym_sphinx::params::{ReplySurbKeyDigestAlgorithm, SphinxKeyRotation}; +use sqlx::FromRow; +use time::OffsetDateTime; #[derive(Debug, Clone)] pub struct StoredSenderTag { @@ -58,11 +61,11 @@ impl TryFrom for (RecipientBytes, AnonymousSenderTag) { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, FromRow)] pub struct StoredReplyKey { pub key_digest: Vec, pub reply_key: Vec, - pub sent_at_timestamp: i64, + pub sent_at: OffsetDateTime, } impl StoredReplyKey { @@ -70,7 +73,7 @@ impl StoredReplyKey { StoredReplyKey { key_digest: key_digest.to_vec(), reply_key: (*reply_key).to_bytes(), - sent_at_timestamp: reply_key.sent_at_timestamp, + sent_at: reply_key.sent_at, } } } @@ -100,32 +103,30 @@ impl TryFrom for (EncryptionKeyDigest, UsedReplyKey) { }); }; - Ok(( - digest, - UsedReplyKey::new(reply_key, value.sent_at_timestamp), - )) + Ok((digest, UsedReplyKey::new(reply_key, value.sent_at))) } } +#[derive(FromRow)] pub struct StoredSurbSender { pub id: i64, pub tag: Vec, - pub last_sent_timestamp: i64, + pub last_sent: OffsetDateTime, } impl StoredSurbSender { - pub fn new(tag: AnonymousSenderTag, last_sent_timestamp: i64) -> Self { + pub fn new(tag: AnonymousSenderTag, last_sent: OffsetDateTime) -> Self { StoredSurbSender { // for the purposes of STORING data, // we ignore that field anyway id: 0, tag: tag.to_bytes().to_vec(), - last_sent_timestamp, + last_sent, } } } -impl TryFrom for (AnonymousSenderTag, i64) { +impl TryFrom for (AnonymousSenderTag, OffsetDateTime) { type Error = StorageError; fn try_from(value: StoredSurbSender) -> Result { @@ -140,7 +141,7 @@ impl TryFrom for (AnonymousSenderTag, i64) { Ok(( AnonymousSenderTag::from_bytes(sender_tag_bytes), - value.last_sent_timestamp, + value.last_sent, )) } } @@ -155,10 +156,10 @@ pub struct StoredReplySurb { } impl StoredReplySurb { - pub fn new(reply_surb_sender_id: i64, reply_surb: &ReplySurbWithKeyRotation) -> Self { + pub fn new(reply_surb_sender_id: i64, reply_surb: &ReceivedReplySurb) -> Self { StoredReplySurb { reply_surb_sender_id, - reply_surb: reply_surb.inner_reply_surb().to_bytes(), + reply_surb: reply_surb.surb.inner_reply_surb().to_bytes(), encoded_key_rotation: reply_surb.key_rotation() as u8, } } diff --git a/common/client-core/surb-storage/src/backend/mod.rs b/common/client-core/surb-storage/src/backend/mod.rs index 8bef5a9aaf..b848552168 100644 --- a/common/client-core/surb-storage/src/backend/mod.rs +++ b/common/client-core/surb-storage/src/backend/mod.rs @@ -5,6 +5,7 @@ use crate::CombinedReplyStorage; use async_trait::async_trait; use std::error::Error; use thiserror::Error; +use time::OffsetDateTime; // TODO: this should now live inside our wasm/client-core pub mod browser_backend; @@ -53,7 +54,10 @@ impl ReplyStorageBackend for Empty { Ok(()) } - async fn load_surb_storage(&self) -> Result { + async fn load_surb_storage( + &self, + _: OffsetDateTime, + ) -> Result { Ok(CombinedReplyStorage::new( self.min_surb_threshold, self.max_surb_threshold, @@ -80,7 +84,10 @@ pub trait ReplyStorageBackend: Sized { /// (such as surb thresholds) async fn init_fresh(&mut self, fresh: &CombinedReplyStorage) -> Result<(), Self::StorageError>; - async fn load_surb_storage(&self) -> Result; + async fn load_surb_storage( + &self, + surb_freshness_cutoff: OffsetDateTime, + ) -> Result; async fn stop_storage_session(self) -> Result<(), Self::StorageError> { Ok(()) diff --git a/common/client-core/surb-storage/src/combined.rs b/common/client-core/surb-storage/src/combined.rs index 9d44e53923..51c13bc532 100644 --- a/common/client-core/surb-storage/src/combined.rs +++ b/common/client-core/surb-storage/src/combined.rs @@ -25,12 +25,11 @@ impl CombinedReplyStorage { pub fn load( sent_reply_keys: SentReplyKeys, received_reply_surbs: ReceivedReplySurbsMap, - used_tags: UsedSenderTags, ) -> Self { CombinedReplyStorage { sent_reply_keys, received_reply_surbs, - used_tags, + used_tags: UsedSenderTags::new(), } } diff --git a/common/client-core/surb-storage/src/key_storage.rs b/common/client-core/surb-storage/src/key_storage.rs index 61a474ad5c..b62cd87d5f 100644 --- a/common/client-core/surb-storage/src/key_storage.rs +++ b/common/client-core/surb-storage/src/key_storage.rs @@ -47,8 +47,12 @@ impl SentReplyKeys { self.inner.data.iter() } + pub fn retain(&self, f: impl FnMut(&EncryptionKeyDigest, &mut UsedReplyKey) -> bool) { + self.inner.data.retain(f); + } + pub fn insert_multiple(&self, keys: Vec) { - let now = OffsetDateTime::now_utc().unix_timestamp(); + let now = OffsetDateTime::now_utc(); for key in keys { self.insert(UsedReplyKey::new(key, now)) } @@ -71,15 +75,12 @@ impl SentReplyKeys { pub struct UsedReplyKey { key: SurbEncryptionKey, // the purpose of this field is to perform invalidation at relatively very long intervals - pub sent_at_timestamp: i64, + pub sent_at: OffsetDateTime, } impl UsedReplyKey { - pub(crate) fn new(key: SurbEncryptionKey, sent_at_timestamp: i64) -> Self { - UsedReplyKey { - key, - sent_at_timestamp, - } + pub(crate) fn new(key: SurbEncryptionKey, sent_at: OffsetDateTime) -> Self { + UsedReplyKey { key, sent_at } } } diff --git a/common/client-core/surb-storage/src/lib.rs b/common/client-core/surb-storage/src/lib.rs index 9e0ba14b34..3f49f9da9d 100644 --- a/common/client-core/surb-storage/src/lib.rs +++ b/common/client-core/surb-storage/src/lib.rs @@ -4,8 +4,9 @@ pub use backend::*; pub use combined::CombinedReplyStorage; pub use key_storage::SentReplyKeys; -pub use surb_storage::ReceivedReplySurbsMap; +pub use surb_storage::{ReceivedReplySurb, ReceivedReplySurbsMap, RetrievedReplySurb}; pub use tag_storage::UsedSenderTags; +use time::OffsetDateTime; mod backend; mod combined; @@ -29,8 +30,11 @@ where PersistentReplyStorage { backend } } - pub async fn load_state_from_backend(&self) -> Result { - self.backend.load_surb_storage().await + pub async fn load_state_from_backend( + &self, + surb_freshness_cutoff: OffsetDateTime, + ) -> Result { + self.backend.load_surb_storage(surb_freshness_cutoff).await } // this will have to get enabled after merging develop @@ -39,7 +43,7 @@ where mem_state: CombinedReplyStorage, mut shutdown: nym_task::TaskClient, ) { - use log::{debug, error, info}; + use tracing::{debug, error, info}; debug!("Started PersistentReplyStorage"); if let Err(err) = self.backend.start_storage_session().await { diff --git a/common/client-core/surb-storage/src/surb_storage.rs b/common/client-core/surb-storage/src/surb_storage.rs index ac453b2aa1..2852c8b8cb 100644 --- a/common/client-core/surb-storage/src/surb_storage.rs +++ b/common/client-core/surb-storage/src/surb_storage.rs @@ -1,15 +1,45 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use dashmap::iter::Iter; +use dashmap::iter::{Iter, IterMut}; use dashmap::DashMap; -use log::trace; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::anonymous_replies::ReplySurbWithKeyRotation; +use nym_sphinx::params::SphinxKeyRotation; +use std::cmp::min; use std::collections::VecDeque; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use time::OffsetDateTime; +use tracing::{error, info, trace}; + +#[derive(Debug)] +pub struct RetrievedReplySurb { + pub(crate) reply_surb: ReceivedReplySurb, + pub(crate) stale_pile: bool, +} + +impl RetrievedReplySurb { + pub(crate) fn new_fresh(reply_surb: ReceivedReplySurb) -> Self { + RetrievedReplySurb { + reply_surb, + stale_pile: false, + } + } + + pub(crate) fn new_stale(reply_surb: ReceivedReplySurb) -> Self { + RetrievedReplySurb { + reply_surb, + stale_pile: true, + } + } +} + +impl From for ReplySurbWithKeyRotation { + fn from(retrieved: RetrievedReplySurb) -> Self { + retrieved.reply_surb.into() + } +} #[derive(Debug, Clone)] pub struct ReceivedReplySurbsMap { @@ -57,17 +87,40 @@ impl ReceivedReplySurbsMap { self.inner.data.iter() } - pub fn remove(&self, target: &AnonymousSenderTag) { - self.inner.data.remove(target); + pub fn as_raw_iter_mut(&self) -> IterMut<'_, AnonymousSenderTag, ReceivedReplySurbs> { + self.inner.data.iter_mut() } - pub fn reset_surbs_last_received_at(&self, target: &AnonymousSenderTag) { - if let Some(mut entry) = self.inner.data.get_mut(target) { - entry.surbs_last_received_at_timestamp = OffsetDateTime::now_utc().unix_timestamp(); + fn total_surbs(&self) -> usize { + self.inner + .data + .iter() + .map(|entry| entry.value().data.len()) + .sum() + } + + pub fn drop_stale_loaded_surbs(&self, cutoff: OffsetDateTime) { + let before = self.total_surbs(); + self.inner.data.retain(|_, v| { + if v.surbs_last_received_at() < cutoff { + return false; + } + + v.data.retain(|s| s.received_at > cutoff); + !v.data.is_empty() + }); + let after = self.total_surbs(); + let diff = before - after; + if diff != 0 { + info!("removed {diff} stale reply SURBs") } } - pub fn surbs_last_received_at(&self, target: &AnonymousSenderTag) -> Option { + pub fn retain(&self, f: impl FnMut(&AnonymousSenderTag, &mut ReceivedReplySurbs) -> bool) { + self.inner.data.retain(f); + } + + pub fn surbs_last_received_at(&self, target: &AnonymousSenderTag) -> Option { self.inner .data .get(target) @@ -126,15 +179,25 @@ impl ReceivedReplySurbsMap { .unwrap_or_default() } + pub fn available_fresh_surbs(&self, target: &AnonymousSenderTag) -> usize { + self.inner + .data + .get(target) + .map(|entry| entry.fresh_left()) + .unwrap_or_default() + } + pub fn contains_surbs_for(&self, target: &AnonymousSenderTag) -> bool { self.inner.data.contains_key(target) } + /// Attempt to retrieve the specified number of reply SURBs for the target sender + /// and return the number of SURBs remaining in the storage after the call. pub fn get_reply_surbs( &self, target: &AnonymousSenderTag, amount: usize, - ) -> (Option>, usize) { + ) -> (Option>, usize) { if let Some(mut entry) = self.inner.data.get_mut(target) { let surbs_left = entry.items_left(); if surbs_left < self.min_surb_threshold() + amount { @@ -150,34 +213,72 @@ impl ReceivedReplySurbsMap { pub fn get_reply_surb_ignoring_threshold( &self, target: &AnonymousSenderTag, - ) -> Option<(Option, usize)> { - self.inner - .data - .get_mut(target) - .map(|mut s| s.get_reply_surb()) + ) -> (Option, usize) { + let Some(mut entry) = self.inner.data.get_mut(target) else { + return (None, 0); + }; + + entry.get_reply_surb() } pub fn get_reply_surb( &self, target: &AnonymousSenderTag, - ) -> Option<(Option, usize)> { - self.inner.data.get_mut(target).map(|mut entry| { - let surbs_left = entry.items_left(); - if surbs_left < self.min_surb_threshold() { - (None, surbs_left) - } else { - entry.get_reply_surb() - } - }) + ) -> (Option, usize) { + let Some(mut entry) = self.inner.data.get_mut(target) else { + return (None, 0); + }; + + let surbs_left = entry.items_left(); + if surbs_left < self.min_surb_threshold() { + (None, surbs_left) + } else { + entry.get_reply_surb() + } } - pub fn insert_surbs>( + pub fn re_insert_reply_surbs( + &self, + target: &AnonymousSenderTag, + surbs: Vec, + ) { + error!("re-inserting {} unused surbs", surbs.len()); + let mut entry = self.inner.data.entry(*target).or_insert_with(|| { + // this branch should realistically NEVER happen, but software be software, so let's not crash + error!("attempting to return surbs to no longer existing entry {target}"); + ReceivedReplySurbs::new(VecDeque::new()) + }); + + let entry = entry.value_mut(); + for returned_surb in surbs.into_iter().rev() { + if returned_surb.stale_pile { + entry.possibly_stale.push_front(returned_surb.reply_surb) + } else { + entry.data.push_front(returned_surb.reply_surb) + } + } + } + + pub fn insert_fresh_surbs>( &self, target: &AnonymousSenderTag, surbs: I, ) { if let Some(mut existing_data) = self.inner.data.get_mut(target) { - existing_data.insert_reply_surbs(surbs) + existing_data.insert_fresh_reply_surbs(surbs); + + if existing_data.possibly_stale.is_empty() { + return; + } + + // if we're above the minimum threshold, remove stale surbs + let threshold = self.min_surb_threshold(); + let diff = existing_data.data.len().saturating_sub(threshold); + + trace!("will attempt to remove up to {diff} stale surbs"); + if diff > 0 { + existing_data.remove_stale_surbs(diff); + } } else { let new_entry = ReceivedReplySurbs::new(surbs.into_iter().collect()); self.inner.data.insert(*target, new_entry); @@ -185,44 +286,102 @@ impl ReceivedReplySurbsMap { } } +#[derive(Debug)] +pub struct ReceivedReplySurb { + pub(crate) surb: ReplySurbWithKeyRotation, + pub(crate) received_at: OffsetDateTime, +} + +impl From for ReplySurbWithKeyRotation { + fn from(surb: ReceivedReplySurb) -> Self { + surb.surb + } +} + +impl ReceivedReplySurb { + pub fn received_at(&self) -> OffsetDateTime { + self.received_at + } + + pub fn key_rotation(&self) -> SphinxKeyRotation { + self.surb.key_rotation() + } +} + #[derive(Debug)] pub struct ReceivedReplySurbs { - // in the future we'd probably want to put extra data here to indicate when the SURBs got received - // so we could invalidate entries from the previous key rotations - data: VecDeque, + data: VecDeque, + possibly_stale: VecDeque, pending_reception: u32, - surbs_last_received_at_timestamp: i64, + surbs_last_received_at: OffsetDateTime, } impl ReceivedReplySurbs { fn new(initial_surbs: VecDeque) -> Self { - ReceivedReplySurbs { - data: initial_surbs, + let mut this = ReceivedReplySurbs { + data: Default::default(), + possibly_stale: Default::default(), pending_reception: 0, - surbs_last_received_at_timestamp: OffsetDateTime::now_utc().unix_timestamp(), - } + surbs_last_received_at: OffsetDateTime::now_utc(), + }; + this.insert_fresh_reply_surbs(initial_surbs); + this } #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] pub fn new_retrieved( surbs: Vec, - surbs_last_received_at_timestamp: i64, + surbs_last_received_at: OffsetDateTime, ) -> ReceivedReplySurbs { - ReceivedReplySurbs { - data: surbs.into(), + let mut this = ReceivedReplySurbs { + data: Default::default(), + possibly_stale: Default::default(), pending_reception: 0, - surbs_last_received_at_timestamp, - } + surbs_last_received_at, + }; + this.insert_fresh_reply_surbs(surbs); + this.surbs_last_received_at = surbs_last_received_at; + this + } + + pub fn downgrade_freshness(&mut self) -> usize { + debug_assert!(self.possibly_stale.is_empty()); + std::mem::swap(&mut self.data, &mut self.possibly_stale); + self.possibly_stale.len() + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() && self.possibly_stale.is_empty() } #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] - pub fn surbs_ref(&self) -> &VecDeque { + pub fn surbs_ref(&self) -> &VecDeque { &self.data } - pub fn surbs_last_received_at(&self) -> i64 { - self.surbs_last_received_at_timestamp + pub fn retain_fresh_surbs(&mut self, f: impl FnMut(&ReceivedReplySurb) -> bool) { + self.data.retain(f); + } + + pub fn retain_possibly_stale_surbs(&mut self, f: impl FnMut(&ReceivedReplySurb) -> bool) { + self.possibly_stale.retain(f); + } + + pub fn fresh_left(&self) -> usize { + self.data.len() + } + + pub fn possibly_stale_left(&self) -> usize { + self.possibly_stale.len() + } + + pub fn drop_possibly_stale_surbs(&mut self) { + self.possibly_stale = VecDeque::new(); + } + + pub fn surbs_last_received_at(&self) -> OffsetDateTime { + self.surbs_last_received_at } pub fn pending_reception(&self) -> u32 { @@ -243,39 +402,78 @@ impl ReceivedReplySurbs { self.pending_reception = 0; } - pub fn get_reply_surbs( - &mut self, - amount: usize, - ) -> (Option>, usize) { + /// Attempt to retrieve the specified number of reply SURBs (if at least that many are present) + /// and return the number of SURBs remaining in the storage after the call. + pub fn get_reply_surbs(&mut self, amount: usize) -> (Option>, usize) { if self.items_left() < amount { (None, self.items_left()) } else { - let surbs = self.data.drain(..amount).collect(); - (Some(surbs), self.items_left()) + let available_fresh = self.fresh_left(); + + // prefer the 'fresh' data if available. otherwise fallback to the possibly stale entries + let mut reply_surbs = Vec::with_capacity(amount); + + let fresh_to_retrieve = min(available_fresh, amount); + + for surb in self.data.drain(..fresh_to_retrieve) { + reply_surbs.push(RetrievedReplySurb::new_fresh(surb)) + } + + if available_fresh < amount { + let stale_to_retrieve = amount - fresh_to_retrieve; + for surb in self.possibly_stale.drain(..stale_to_retrieve) { + reply_surbs.push(RetrievedReplySurb::new_stale(surb)) + } + } + + (Some(reply_surbs), self.items_left()) } } - pub fn get_reply_surb(&mut self) -> (Option, usize) { + pub fn get_reply_surb(&mut self) -> (Option, usize) { (self.pop_surb(), self.items_left()) } - fn pop_surb(&mut self) -> Option { - self.data.pop_front() + fn pop_surb(&mut self) -> Option { + // prefer the 'fresh' data if available. otherwise fallback to the possibly stale entries + if let Some(fresh) = self.data.pop_front() { + return Some(RetrievedReplySurb::new_fresh(fresh)); + } + if let Some(stale) = self.possibly_stale.pop_front() { + return Some(RetrievedReplySurb::new_stale(stale)); + } + None } fn items_left(&self) -> usize { - self.data.len() + self.data.len() + self.possibly_stale.len() + } + + pub fn remove_stale_surbs(&mut self, amount: usize) { + // remove up to amount number of possibly stale surbs + let amount = min(amount, self.possibly_stale.len()); + + self.possibly_stale.drain(..amount); } // realistically we're always going to be getting multiple surbs at once - pub fn insert_reply_surbs>( + pub(crate) fn insert_fresh_reply_surbs>( &mut self, surbs: I, ) { - let mut v = surbs.into_iter().collect::>(); + let received_at = OffsetDateTime::now_utc(); + let mut v = surbs + .into_iter() + .map(|surb| ReceivedReplySurb { surb, received_at }) + .collect::>(); + + if v.is_empty() { + return; + } + trace!("storing {} surbs in the storage", v.len()); self.data.append(&mut v); - self.surbs_last_received_at_timestamp = OffsetDateTime::now_utc().unix_timestamp(); + self.surbs_last_received_at = received_at; trace!("we now have {} surbs!", self.data.len()); } } diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index bb4ac82b3c..16d1013c8b 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -72,7 +72,7 @@ macro_rules! collect_paged_skimmed_v2 { .$f(false, Some(page), None, $self.use_bincode) .await?; - if metadata != res.metadata { + if !metadata.consistency_check(&res.metadata) { return Err(ValidatorClientError::InconsistentPagedMetadata); } diff --git a/common/commands/src/utils.rs b/common/commands/src/utils.rs index 0d6e40ffbc..4223263e5a 100644 --- a/common/commands/src/utils.rs +++ b/common/commands/src/utils.rs @@ -49,14 +49,14 @@ pub fn show_error(e: E) where E: Display, { - error!("{}", e); + error!("{e}"); } pub fn show_error_passthrough(e: E) -> E where E: Error + Display, { - error!("{}", e); + error!("{e}"); e } diff --git a/common/commands/src/validator/account/balance.rs b/common/commands/src/validator/account/balance.rs index 4e109ad7b3..26ebd86a52 100644 --- a/common/commands/src/validator/account/balance.rs +++ b/common/commands/src/validator/account/balance.rs @@ -42,7 +42,7 @@ pub async fn query_balance( .address .unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic")); - info!("Getting balance for {}...", address); + info!("Getting balance for {address}..."); match client.get_all_balances(&address).await { Ok(coins) => { diff --git a/common/commands/src/validator/account/pubkey.rs b/common/commands/src/validator/account/pubkey.rs index 6a7c3383d6..ed419c1101 100644 --- a/common/commands/src/validator/account/pubkey.rs +++ b/common/commands/src/validator/account/pubkey.rs @@ -57,17 +57,17 @@ pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip3 println!("{}", account.public_key().to_string()); } None => { - error!("Could not derive key that matches {}", address) + error!("Could not derive key that matches {address}") } }, Err(e) => { - error!("Failed to derive accounts. {}", e); + error!("Failed to derive accounts. {e}"); } } } pub async fn get_pubkey_from_chain(address: AccountId, client: &QueryClient) { - info!("Getting public key for address {} from chain...", address); + info!("Getting public key for address {address} from chain..."); match client.get_account(&address).await { Ok(Some(account)) => { if let Ok(base_account) = account.try_get_base_account() { diff --git a/common/commands/src/validator/account/send_multiple.rs b/common/commands/src/validator/account/send_multiple.rs index 585aa896f3..762efdb8ab 100644 --- a/common/commands/src/validator/account/send_multiple.rs +++ b/common/commands/src/validator/account/send_multiple.rs @@ -37,7 +37,7 @@ pub async fn send_multiple(args: Args, client: &SigningClient) { let rows = InputFileReader::new(&args.input); if let Err(e) = rows { - error!("Failed to read input file: {}", e); + error!("Failed to read input file: {e}"); return; } let rows = rows.unwrap(); @@ -67,7 +67,7 @@ pub async fn send_multiple(args: Args, client: &SigningClient) { .prompt(); if let Err(e) = ans { - info!("Aborting, {}...", e); + info!("Aborting, {e}..."); return; } if let Ok(false) = ans { @@ -100,13 +100,10 @@ pub async fn send_multiple(args: Args, client: &SigningClient) { println!("Transaction hash: {}", &res.hash); if let Some(output_filename) = args.output { - println!("\nWriting output log to {}", output_filename); + println!("\nWriting output log to {output_filename}"); if let Err(e) = write_output_file(rows, res, &output_filename) { - error!( - "Failed to write output file {} with error {}", - output_filename, e - ); + error!("Failed to write output file {output_filename} with error {e}"); } } } @@ -136,7 +133,7 @@ fn write_output_file( .collect::>() .join("\n"); - Ok(file.write_all(format!("{}\n", data).as_bytes())?) + Ok(file.write_all(format!("{data}\n").as_bytes())?) } #[derive(Debug)] @@ -171,7 +168,7 @@ impl InputFileReader { // multiply when a whole token amount, e.g. 50nym (50.123456nym is not allowed, that must be input as 50123456unym) let (amount, denom) = if !denom.starts_with('u') { - (amount * 1_000_000u128, format!("u{}", denom)) + (amount * 1_000_000u128, format!("u{denom}")) } else { (amount, denom) }; diff --git a/common/commands/src/validator/cosmwasm/execute_contract.rs b/common/commands/src/validator/cosmwasm/execute_contract.rs index 0dfc547430..e5faa5cd03 100644 --- a/common/commands/src/validator/cosmwasm/execute_contract.rs +++ b/common/commands/src/validator/cosmwasm/execute_contract.rs @@ -55,6 +55,6 @@ pub async fn execute(args: Args, client: SigningClient) { .await { Ok(res) => info!("SUCCESS ✅\n{}", json!(res)), - Err(e) => error!("FAILURE ❌\n{}", e), + Err(e) => error!("FAILURE ❌\n{e}"), } } diff --git a/common/commands/src/validator/cosmwasm/generators/coconut_dkg.rs b/common/commands/src/validator/cosmwasm/generators/coconut_dkg.rs index 3a80ff4ca5..5f1b57b019 100644 --- a/common/commands/src/validator/cosmwasm/generators/coconut_dkg.rs +++ b/common/commands/src/validator/cosmwasm/generators/coconut_dkg.rs @@ -43,7 +43,7 @@ pub struct Args { pub async fn generate(args: Args) { info!("Starting to generate vesting contract instantiate msg"); - debug!("Received arguments: {:?}", args); + debug!("Received arguments: {args:?}"); let multisig_addr = args.multisig_addr.unwrap_or_else(|| { let address = std::env::var(nym_network_defaults::var_names::REWARDING_VALIDATOR_ADDRESS) @@ -97,7 +97,7 @@ pub async fn generate(args: Args) { key_size: DEFAULT_DEALINGS as u32, }; - debug!("instantiate_msg: {:?}", instantiate_msg); + debug!("instantiate_msg: {instantiate_msg:?}"); let res = serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json"); diff --git a/common/commands/src/validator/cosmwasm/generators/ecash_bandwidth.rs b/common/commands/src/validator/cosmwasm/generators/ecash_bandwidth.rs index f13bb65151..52244d761b 100644 --- a/common/commands/src/validator/cosmwasm/generators/ecash_bandwidth.rs +++ b/common/commands/src/validator/cosmwasm/generators/ecash_bandwidth.rs @@ -28,7 +28,7 @@ pub struct Args { pub async fn generate(args: Args) { info!("Starting to generate vesting contract instantiate msg"); - debug!("Received arguments: {:?}", args); + debug!("Received arguments: {args:?}"); let group_addr = args.group_addr.unwrap_or_else(|| { let address = std::env::var(nym_network_defaults::var_names::GROUP_CONTRACT_ADDRESS) @@ -51,7 +51,7 @@ pub async fn generate(args: Args) { deposit_amount: args.deposit_amount, }; - debug!("instantiate_msg: {:?}", instantiate_msg); + debug!("instantiate_msg: {instantiate_msg:?}"); let res = serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json"); diff --git a/common/commands/src/validator/cosmwasm/generators/mixnet.rs b/common/commands/src/validator/cosmwasm/generators/mixnet.rs index 8a8081400e..9226a02653 100644 --- a/common/commands/src/validator/cosmwasm/generators/mixnet.rs +++ b/common/commands/src/validator/cosmwasm/generators/mixnet.rs @@ -88,7 +88,7 @@ pub struct Args { pub async fn generate(args: Args) { info!("Starting to generate mixnet contract instantiate msg"); - debug!("Received arguments: {:?}", args); + debug!("Received arguments: {args:?}"); let initial_rewarding_params = InitialRewardingParams { initial_reward_pool: Decimal::from_atomics(args.initial_reward_pool, 0) @@ -114,7 +114,7 @@ pub async fn generate(args: Args) { }, }; - debug!("initial_rewarding_params: {:?}", initial_rewarding_params); + debug!("initial_rewarding_params: {initial_rewarding_params:?}"); let rewarding_validator_address = args.rewarding_validator_address.unwrap_or_else(|| { let address = std::env::var(nym_network_defaults::var_names::REWARDING_VALIDATOR_ADDRESS) @@ -160,7 +160,7 @@ pub async fn generate(args: Args) { key_validity_in_epochs: None, }; - debug!("instantiate_msg: {:?}", instantiate_msg); + debug!("instantiate_msg: {instantiate_msg:?}"); let res = serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json"); diff --git a/common/commands/src/validator/cosmwasm/generators/multisig.rs b/common/commands/src/validator/cosmwasm/generators/multisig.rs index 90abae9e28..8b0b79e4fe 100644 --- a/common/commands/src/validator/cosmwasm/generators/multisig.rs +++ b/common/commands/src/validator/cosmwasm/generators/multisig.rs @@ -31,7 +31,7 @@ pub struct Args { pub async fn generate(args: Args) { info!("Starting to generate vesting contract instantiate msg"); - debug!("Received arguments: {:?}", args); + debug!("Received arguments: {args:?}"); let ecash_contract_address = args.ecash_contract_address.unwrap_or_else(|| { let address = std::env::var(nym_network_defaults::var_names::ECASH_CONTRACT_ADDRESS) @@ -60,7 +60,7 @@ pub async fn generate(args: Args) { coconut_dkg_contract_address: coconut_dkg_contract_address.to_string(), }; - debug!("instantiate_msg: {:?}", instantiate_msg); + debug!("instantiate_msg: {instantiate_msg:?}"); let res = serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json"); diff --git a/common/commands/src/validator/cosmwasm/generators/vesting.rs b/common/commands/src/validator/cosmwasm/generators/vesting.rs index 94f5cda0b2..536520fa9c 100644 --- a/common/commands/src/validator/cosmwasm/generators/vesting.rs +++ b/common/commands/src/validator/cosmwasm/generators/vesting.rs @@ -21,7 +21,7 @@ pub struct Args { pub async fn generate(args: Args) { info!("Starting to generate vesting contract instantiate msg"); - debug!("Received arguments: {:?}", args); + debug!("Received arguments: {args:?}"); let mixnet_contract_address = args.mixnet_contract_address.unwrap_or_else(|| { let address = std::env::var(nym_network_defaults::var_names::MIXNET_CONTRACT_ADDRESS) @@ -39,7 +39,7 @@ pub async fn generate(args: Args) { mix_denom, }; - debug!("instantiate_msg: {:?}", instantiate_msg); + debug!("instantiate_msg: {instantiate_msg:?}"); let res = serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json"); diff --git a/common/commands/src/validator/cosmwasm/init_contract.rs b/common/commands/src/validator/cosmwasm/init_contract.rs index 430440be44..ca239d8f1a 100644 --- a/common/commands/src/validator/cosmwasm/init_contract.rs +++ b/common/commands/src/validator/cosmwasm/init_contract.rs @@ -72,7 +72,7 @@ pub async fn init(args: Args, client: SigningClient, network_details: &NymNetwor .await .expect("failed to instantiate the contract!"); - info!("Init result: {:?}", res); + info!("Init result: {res:?}"); println!("{}", res.contract_address) } diff --git a/common/commands/src/validator/cosmwasm/migrate_contract.rs b/common/commands/src/validator/cosmwasm/migrate_contract.rs index f106ae8b17..2dd38ccdd8 100644 --- a/common/commands/src/validator/cosmwasm/migrate_contract.rs +++ b/common/commands/src/validator/cosmwasm/migrate_contract.rs @@ -47,5 +47,5 @@ pub async fn migrate(args: Args, client: SigningClient) { .expect("failed to migrate the contract!") }; - info!("Migrate result: {:?}", res); + info!("Migrate result: {res:?}"); } diff --git a/common/commands/src/validator/cosmwasm/upload_contract.rs b/common/commands/src/validator/cosmwasm/upload_contract.rs index 96d01741ee..5a17b66013 100644 --- a/common/commands/src/validator/cosmwasm/upload_contract.rs +++ b/common/commands/src/validator/cosmwasm/upload_contract.rs @@ -31,7 +31,7 @@ pub async fn upload(args: Args, client: SigningClient) { .await .expect("failed to upload the contract!"); - info!("Upload result: {:?}", res); + info!("Upload result: {res:?}"); println!("{}", res.code_id) } diff --git a/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs b/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs index dc8a2d591f..f3b99513f7 100644 --- a/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs @@ -47,5 +47,5 @@ pub async fn delegate_to_mixnode(args: Args, client: SigningClient) { .await .expect("failed to delegate to mixnode!"); - info!("delegating to mixnode: {:?}", res); + info!("delegating to mixnode: {res:?}"); } diff --git a/common/commands/src/validator/mixnet/delegators/delegate_to_multiple_mixnodes.rs b/common/commands/src/validator/mixnet/delegators/delegate_to_multiple_mixnodes.rs index 96bf95be04..70eb85e7cd 100644 --- a/common/commands/src/validator/mixnet/delegators/delegate_to_multiple_mixnodes.rs +++ b/common/commands/src/validator/mixnet/delegators/delegate_to_multiple_mixnodes.rs @@ -196,7 +196,7 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) { let records = match InputFileReader::new(&args.input) { Ok(records) => records, Err(e) => { - println!("Error reading input file: {}", e); + println!("Error reading input file: {e}"); return; } }; @@ -262,11 +262,11 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) { } if !undelegation_msgs.is_empty() { - println!("Undelegation records : \n{}\n\n", undelegation_table); + println!("Undelegation records : \n{undelegation_table}\n\n"); } if !delegation_msgs.is_empty() { - println!("Delegation records : \n{}\n\n", delegation_table); + println!("Delegation records : \n{delegation_table}\n\n"); } let ans = inquire::Confirm::new("Do you want to continue with the shown operations?") @@ -275,7 +275,7 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) { .prompt(); if let Err(e) = ans { - info!("Aborting, {}...", e); + info!("Aborting, {e}..."); return; } @@ -348,7 +348,7 @@ pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) { if args.output.is_some() { if let Err(e) = write_to_csv(output_details, args.output) { - info!("Failed to write to CSV, {}", e); + info!("Failed to write to CSV, {e}"); } } } diff --git a/common/commands/src/validator/mixnet/delegators/migrate_vested_delegation.rs b/common/commands/src/validator/mixnet/delegators/migrate_vested_delegation.rs index 8ac1ab9c07..66a2a5d659 100644 --- a/common/commands/src/validator/mixnet/delegators/migrate_vested_delegation.rs +++ b/common/commands/src/validator/mixnet/delegators/migrate_vested_delegation.rs @@ -38,5 +38,5 @@ pub async fn migrate_vested_delegation(args: Args, client: SigningClient) { .await .expect("failed to migrate delegation!"); - info!("migration result: {:?}", res) + info!("migration result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs b/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs index 83426a316e..8b876831af 100644 --- a/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs +++ b/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs @@ -40,5 +40,5 @@ pub async fn claim_delegator_reward(args: Args, client: SigningClient) { .await .expect("failed to claim delegator-reward"); - info!("Claiming delegator reward: {:?}", res) + info!("Claiming delegator reward: {res:?}") } diff --git a/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs b/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs index d36c4a7b17..b0e9df21bd 100644 --- a/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs +++ b/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs @@ -40,5 +40,5 @@ pub async fn vesting_claim_delegator_reward(args: Args, client: SigningClient) { .await .expect("failed to claim vesting delegator-reward"); - info!("Claiming vesting delegator reward: {:?}", res) + info!("Claiming vesting delegator reward: {res:?}") } diff --git a/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs b/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs index 19593ed5ce..edcd646e90 100644 --- a/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs @@ -40,5 +40,5 @@ pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) { .await .expect("failed to remove stake from mixnode!"); - info!("removing stake from mixnode: {:?}", res) + info!("removing stake from mixnode: {res:?}") } diff --git a/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs b/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs index 3fa3fd7cee..28351541f5 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs @@ -53,5 +53,5 @@ pub async fn vesting_delegate_to_mixnode(args: Args, client: SigningClient) { .await .expect("failed to delegate to mixnode!"); - info!("vesting delegating to mixnode: {:?}", res); + info!("vesting delegating to mixnode: {res:?}"); } diff --git a/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs b/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs index 9daf8691d3..c02f87e055 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs @@ -45,5 +45,5 @@ pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient) .await .expect("failed to remove stake from vesting account on mixnode!"); - info!("removing stake from vesting mixnode: {:?}", res) + info!("removing stake from vesting mixnode: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs index 28fe160070..c11246c46e 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs @@ -73,5 +73,5 @@ pub async fn bond_gateway(args: Args, client: SigningClient) { .await .expect("failed to bond gateway!"); - info!("Bonding result: {:?}", res) + info!("Bonding result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/gateway/nymnode_migration.rs b/common/commands/src/validator/mixnet/operators/gateway/nymnode_migration.rs index a6b22a2d4b..c1189f1885 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/nymnode_migration.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/nymnode_migration.rs @@ -52,5 +52,5 @@ pub async fn migrate_to_nymnode(args: Args, client: SigningClient) { .await .expect("failed to migrate gateway!"); - info!("migration result: {:?}", res) + info!("migration result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs index c0cf879147..a1b5a8d4bd 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs @@ -56,5 +56,5 @@ pub async fn update_config(args: Args, client: SigningClient) { .await .expect("updating gateway config"); - info!("gateway config updated: {:?}", res) + info!("gateway config updated: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs index 346b652b2f..8ebc9279bf 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs @@ -57,5 +57,5 @@ pub async fn vesting_update_config(args: Args, client: SigningClient) { .await .expect("updating vesting gateway config"); - info!("gateway config updated: {:?}", res) + info!("gateway config updated: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs index af9c6c8bb4..cef6dbcf7f 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs @@ -17,5 +17,5 @@ pub async fn unbond_gateway(client: SigningClient) { .await .expect("failed to unbond gateway!"); - info!("Unbonding result: {:?}", res) + info!("Unbonding result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs index 91c2817be8..4f5f63d16b 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs @@ -73,5 +73,5 @@ pub async fn vesting_bond_gateway(args: Args, client: SigningClient) { .await .expect("failed to bond gateway!"); - info!("Vesting bonding gateway result: {:?}", res) + info!("Vesting bonding gateway result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs index 1c4312424c..72b9357dab 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs @@ -17,5 +17,5 @@ pub async fn vesting_unbond_gateway(client: SigningClient) { .await .expect("failed to unbond vesting gateway!"); - info!("Unbonding vesting result: {:?}", res) + info!("Unbonding vesting result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs index 410ce91d28..b8de001f2d 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs @@ -106,5 +106,5 @@ pub async fn bond_mixnode(args: Args, client: SigningClient) { .await .expect("failed to bond mixnode!"); - info!("Bonding result: {:?}", res) + info!("Bonding result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs b/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs index 556021dcf9..bd96aeb270 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs @@ -25,5 +25,5 @@ pub async fn decrease_pledge(args: Args, client: SigningClient) { .await .expect("failed to decrease pledge!"); - info!("decreasing pledge: {:?}", res); + info!("decreasing pledge: {res:?}"); } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/migrate_vested_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/migrate_vested_mixnode.rs index 95bd9d0573..ae2ec7395d 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/migrate_vested_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/migrate_vested_mixnode.rs @@ -15,5 +15,5 @@ pub async fn migrate_vested_mixnode(_args: Args, client: SigningClient) { .await .expect("failed to migrate mixnode!"); - info!("migration result: {:?}", res) + info!("migration result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/nymnode_migration.rs b/common/commands/src/validator/mixnet/operators/mixnode/nymnode_migration.rs index fe46e2c11a..3f736c86f4 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/nymnode_migration.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/nymnode_migration.rs @@ -15,5 +15,5 @@ pub async fn migrate_to_nymnode(_args: Args, client: SigningClient) { .await .expect("failed to migrate mixnode!"); - info!("migration result: {:?}", res) + info!("migration result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs b/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs index 1989725907..51ba9daa4d 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs @@ -25,5 +25,5 @@ pub async fn pledge_more(args: Args, client: SigningClient) { .await .expect("failed to pledge more!"); - info!("pledging more: {:?}", res); + info!("pledging more: {res:?}"); } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs index a5f8d65236..f3e97d0df2 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs @@ -17,5 +17,5 @@ pub async fn claim_operator_reward(_args: Args, client: SigningClient) { .await .expect("failed to claim operator reward"); - info!("Claiming operator reward: {:?}", res) + info!("Claiming operator reward: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs index 3b88c6cf5c..38c2ccf41c 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs @@ -20,5 +20,5 @@ pub async fn vesting_claim_operator_reward(client: SigningClient) { .await .expect("failed to claim vesting operator reward"); - info!("Claiming vesting operator reward: {:?}", res) + info!("Claiming vesting operator reward: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs index 733dea04a1..31db3435b2 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs @@ -64,5 +64,5 @@ pub async fn update_config(args: Args, client: SigningClient) { .await .expect("updating mix-node config"); - info!("mixnode config updated: {:?}", res) + info!("mixnode config updated: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs index a495c8f4b5..092aa54d82 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs @@ -65,5 +65,5 @@ pub async fn vesting_update_config(client: SigningClient, args: Args) { .await .expect("updating vesting mix-node config"); - info!("mixnode config updated: {:?}", res) + info!("mixnode config updated: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs index fe6f3df223..bf6864ef5e 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs @@ -18,5 +18,5 @@ pub async fn unbond_mixnode(_args: Args, client: SigningClient) { .await .expect("failed to unbond mixnode!"); - info!("Unbonding result: {:?}", res) + info!("Unbonding result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs index d55d6463e7..6813ea2dfd 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs @@ -106,5 +106,5 @@ pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str .await .expect("failed to bond vesting mixnode!"); - info!("Bonding vesting result: {:?}", res) + info!("Bonding vesting result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs index bb83e1ffaa..1ffb1de360 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs @@ -25,5 +25,5 @@ pub async fn vesting_decrease_pledge(args: Args, client: SigningClient) { .await .expect("failed to vesting decrease pledge!"); - info!("vesting decreasing pledge: {:?}", res); + info!("vesting decreasing pledge: {res:?}"); } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs index 9917e02860..e53c9b9cfe 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs @@ -26,5 +26,5 @@ pub async fn vesting_pledge_more(args: Args, client: SigningClient) { .await .expect("failed to pledge more!"); - info!("vesting pledge more: {:?}", res); + info!("vesting pledge more: {res:?}"); } diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs index 3e63846430..b3d52bdb73 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs @@ -20,5 +20,5 @@ pub async fn vesting_unbond_mixnode(client: SigningClient) { .await .expect("failed to unbond vesting mixnode!"); - info!("Unbonding vesting result: {:?}", res) + info!("Unbonding vesting result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/nymnode/bond_nymnode.rs b/common/commands/src/validator/mixnet/operators/nymnode/bond_nymnode.rs index acde3d39e3..145e245863 100644 --- a/common/commands/src/validator/mixnet/operators/nymnode/bond_nymnode.rs +++ b/common/commands/src/validator/mixnet/operators/nymnode/bond_nymnode.rs @@ -85,5 +85,5 @@ pub async fn bond_nymnode(args: Args, client: SigningClient) { .await .expect("failed to bond nymnode!"); - info!("Bonding result: {:?}", res) + info!("Bonding result: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/nymnode/pledge/decrease_pledge.rs b/common/commands/src/validator/mixnet/operators/nymnode/pledge/decrease_pledge.rs index a299ddbe65..4bb20b4a66 100644 --- a/common/commands/src/validator/mixnet/operators/nymnode/pledge/decrease_pledge.rs +++ b/common/commands/src/validator/mixnet/operators/nymnode/pledge/decrease_pledge.rs @@ -25,5 +25,5 @@ pub async fn decrease_pledge(args: Args, client: SigningClient) { .await .expect("failed to decrease pledge!"); - info!("decreasing pledge: {:?}", res); + info!("decreasing pledge: {res:?}"); } diff --git a/common/commands/src/validator/mixnet/operators/nymnode/pledge/increase_pledge.rs b/common/commands/src/validator/mixnet/operators/nymnode/pledge/increase_pledge.rs index 09b94bc5d5..31a0eb7a8d 100644 --- a/common/commands/src/validator/mixnet/operators/nymnode/pledge/increase_pledge.rs +++ b/common/commands/src/validator/mixnet/operators/nymnode/pledge/increase_pledge.rs @@ -25,5 +25,5 @@ pub async fn increase_pledge(args: Args, client: SigningClient) { .await .expect("failed to pledge more!"); - info!("pledging more: {:?}", res); + info!("pledging more: {res:?}"); } diff --git a/common/commands/src/validator/mixnet/operators/nymnode/rewards/claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/nymnode/rewards/claim_operator_reward.rs index a8f157f661..005e81f069 100644 --- a/common/commands/src/validator/mixnet/operators/nymnode/rewards/claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/nymnode/rewards/claim_operator_reward.rs @@ -17,5 +17,5 @@ pub async fn claim_operator_reward(_args: Args, client: SigningClient) { .await .expect("failed to claim operator reward"); - info!("Claiming operator reward: {:?}", res) + info!("Claiming operator reward: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/nymnode/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/nymnode/settings/update_config.rs index 5ba5bf52fa..fb59924c9e 100644 --- a/common/commands/src/validator/mixnet/operators/nymnode/settings/update_config.rs +++ b/common/commands/src/validator/mixnet/operators/nymnode/settings/update_config.rs @@ -46,5 +46,5 @@ pub async fn update_config(args: Args, client: SigningClient) { .await .expect("updating nym node config"); - info!("nym node config updated: {:?}", res) + info!("nym node config updated: {res:?}") } diff --git a/common/commands/src/validator/mixnet/operators/nymnode/settings/update_cost_params.rs b/common/commands/src/validator/mixnet/operators/nymnode/settings/update_cost_params.rs index 5668c92e0d..a513067a02 100644 --- a/common/commands/src/validator/mixnet/operators/nymnode/settings/update_cost_params.rs +++ b/common/commands/src/validator/mixnet/operators/nymnode/settings/update_cost_params.rs @@ -68,6 +68,6 @@ pub async fn update_cost_params(args: Args, client: SigningClient) -> anyhow::Re .await .expect("failed to update cost params"); - info!("Cost params result: {:?}", res); + info!("Cost params result: {res:?}"); Ok(()) } diff --git a/common/commands/src/validator/mixnet/operators/nymnode/unbond_nymnode.rs b/common/commands/src/validator/mixnet/operators/nymnode/unbond_nymnode.rs index fbcef6bfd7..d60266a12d 100644 --- a/common/commands/src/validator/mixnet/operators/nymnode/unbond_nymnode.rs +++ b/common/commands/src/validator/mixnet/operators/nymnode/unbond_nymnode.rs @@ -18,5 +18,5 @@ pub async fn unbond_nymnode(_args: Args, client: SigningClient) { .await .expect("failed to unbond Nym Node!"); - info!("Unbonding result: {:?}", res) + info!("Unbonding result: {res:?}") } diff --git a/common/commands/src/validator/signature/sign.rs b/common/commands/src/validator/signature/sign.rs index 424dfd184b..7df1d12eb6 100644 --- a/common/commands/src/validator/signature/sign.rs +++ b/common/commands/src/validator/signature/sign.rs @@ -52,7 +52,7 @@ pub fn sign(args: Args, prefix: &str, mnemonic: Option) { println!("{}", json!(output)); } Err(e) => { - error!("Failed to sign message. {}", e); + error!("Failed to sign message. {e}"); } } } @@ -61,7 +61,7 @@ pub fn sign(args: Args, prefix: &str, mnemonic: Option) { } }, Err(e) => { - error!("Failed to derive accounts. {}", e); + error!("Failed to derive accounts. {e}"); } } } diff --git a/common/commands/src/validator/signature/verify.rs b/common/commands/src/validator/signature/verify.rs index 391c5105d8..7168b75b92 100644 --- a/common/commands/src/validator/signature/verify.rs +++ b/common/commands/src/validator/signature/verify.rs @@ -38,7 +38,7 @@ pub async fn verify(args: Args, client: &QueryClient) { let public_key = match AccountId::from_str(&args.public_key_or_address) { Ok(address) => { - info!("Found account address instead of public key, so looking up public key for {} from chain", address); + info!("Found account address instead of public key, so looking up public key for {address} from chain"); match client.get_account_public_key(&address).await.ok() { Some(public_key) => { if let Some(k) = public_key { @@ -48,8 +48,7 @@ pub async fn verify(args: Args, client: &QueryClient) { } None => { error!( - "Address {} does not have a public key recorded on the chain. This is probably because the account has never signed a transaction.", - address + "Address {address} does not have a public key recorded on the chain. This is probably because the account has never signed a transaction." ); None } @@ -58,7 +57,7 @@ pub async fn verify(args: Args, client: &QueryClient) { Err(_) => match PublicKey::from_json(&args.public_key_or_address) { Ok(parsed) => Some(parsed), Err(e) => { - error!("Public key should be JSON. Unable to parse: {}", e); + error!("Public key should be JSON. Unable to parse: {e}"); None } }, @@ -78,7 +77,7 @@ pub async fn verify(args: Args, client: &QueryClient) { ) { Ok(()) => println!("SUCCESS ✅ signature verified"), Err(e) => { - error!("FAILURE ❌ Signature verification failed: {}", e); + error!("FAILURE ❌ Signature verification failed: {e}"); } } } diff --git a/common/commands/src/validator/vesting/create_vesting_schedule.rs b/common/commands/src/validator/vesting/create_vesting_schedule.rs index 35ee669dab..b166202af0 100644 --- a/common/commands/src/validator/vesting/create_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/create_vesting_schedule.rs @@ -86,6 +86,6 @@ pub async fn create(args: Args, client: SigningClient, network_details: &NymNetw .await .unwrap(); - info!("Vesting result: {:?}", res); - info!("Coin send result: {:?}", send_coin_response); + info!("Vesting result: {res:?}"); + info!("Coin send result: {send_coin_response:?}"); } diff --git a/common/commands/src/validator/vesting/query_vesting_schedule.rs b/common/commands/src/validator/vesting/query_vesting_schedule.rs index 0baca858f1..df3736b94d 100644 --- a/common/commands/src/validator/vesting/query_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/query_vesting_schedule.rs @@ -29,7 +29,7 @@ pub async fn query(args: Args, client: QueryClient, address_from_mnemonic: Optio .address .unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic")); - info!("Checking account {} for a vesting schedule...", account_id); + info!("Checking account {account_id} for a vesting schedule..."); let vesting_address = account_id.to_string(); let denom = client.current_chain_details().mix_denom.base.as_str(); diff --git a/common/credential-utils/src/utils.rs b/common/credential-utils/src/utils.rs index be7133b4c2..1aeab19ed8 100644 --- a/common/credential-utils/src/utils.rs +++ b/common/credential-utils/src/utils.rs @@ -95,7 +95,7 @@ where } else if let Some(final_timestamp) = epoch.final_timestamp_secs() { // Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries let secs_until_final = final_timestamp.saturating_sub(current_timestamp_secs) + 1; - info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final); + info!("Approximately {secs_until_final} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment."); tokio::time::sleep(Duration::from_secs(secs_until_final)).await; } else if matches!(epoch.state, EpochState::WaitingInitialisation) { info!("dkg hasn't been initialised yet and it is not known when it will be. Going to check again later"); diff --git a/common/gateway-stats-storage/build.rs b/common/gateway-stats-storage/build.rs index 166349c67a..cef40420c9 100644 --- a/common/gateway-stats-storage/build.rs +++ b/common/gateway-stats-storage/build.rs @@ -7,9 +7,9 @@ use std::env; #[tokio::main] async fn main() { let out_dir = env::var("OUT_DIR").unwrap(); - let database_path = format!("{}/gateway-stats-example.sqlite", out_dir); + let database_path = format!("{out_dir}/gateway-stats-example.sqlite"); - let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path)) + let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc")) .await .expect("Failed to create SQLx database connection"); diff --git a/common/gateway-storage/build.rs b/common/gateway-storage/build.rs index 27d55fccd2..9b46e07840 100644 --- a/common/gateway-storage/build.rs +++ b/common/gateway-storage/build.rs @@ -7,9 +7,9 @@ use std::env; #[tokio::main] async fn main() { let out_dir = env::var("OUT_DIR").unwrap(); - let database_path = format!("{}/gateway-example.sqlite", out_dir); + let database_path = format!("{out_dir}/gateway-example.sqlite"); - let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path)) + let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc")) .await .expect("Failed to create SQLx database connection"); diff --git a/common/gateway-storage/src/clients.rs b/common/gateway-storage/src/clients.rs index 96b5f857eb..8bee61e04b 100644 --- a/common/gateway-storage/src/clients.rs +++ b/common/gateway-storage/src/clients.rs @@ -37,7 +37,7 @@ impl std::fmt::Display for ClientType { ClientType::EntryWireguard => "entry_wireguard", ClientType::ExitWireguard => "exit_wireguard", }; - write!(f, "{}", s) + write!(f, "{s}") } } diff --git a/common/http-api-client/src/user_agent.rs b/common/http-api-client/src/user_agent.rs index fee71752bb..54fe6ac24e 100644 --- a/common/http-api-client/src/user_agent.rs +++ b/common/http-api-client/src/user_agent.rs @@ -141,7 +141,7 @@ mod tests { }; assert_eq!( - format!("{}", user_agent), + format!("{user_agent}"), "nym-mixnode/0.11.0/x86_64-unknown-linux-gnu/abcdefg" ); } diff --git a/common/http-api-common/Cargo.toml b/common/http-api-common/Cargo.toml index 1de2a0a8c4..4a405c155e 100644 --- a/common/http-api-common/Cargo.toml +++ b/common/http-api-common/Cargo.toml @@ -19,6 +19,7 @@ colored = { workspace = true, optional = true } futures = { workspace = true, optional = true } mime = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } serde_yaml = { workspace = true, optional = true } subtle = { workspace = true, optional = true } time = { workspace = true, optional = true, features = ["macros"] } diff --git a/common/http-api-common/src/response/json.rs b/common/http-api-common/src/response/json.rs index b2c904b7ee..a1b46e7d50 100644 --- a/common/http-api-common/src/response/json.rs +++ b/common/http-api-common/src/response/json.rs @@ -7,7 +7,6 @@ use axum::http::{header, HeaderValue}; use axum::response::{IntoResponse, Response}; use bytes::{BufMut, BytesMut}; use serde::Serialize; -use utoipa::gen::serde_json; // don't use axum's Json directly as we need to be able to define custom headers #[derive(Debug, Clone, Default)] diff --git a/common/ip-packet-requests/src/v8/request.rs b/common/ip-packet-requests/src/v8/request.rs index c5e49e83b7..964065578b 100644 --- a/common/ip-packet-requests/src/v8/request.rs +++ b/common/ip-packet-requests/src/v8/request.rs @@ -191,7 +191,7 @@ impl fmt::Display for IpPacketRequestData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { IpPacketRequestData::Data(_) => write!(f, "Data"), - IpPacketRequestData::Control(c) => write!(f, "Control({})", c), + IpPacketRequestData::Control(c) => write!(f, "Control({c})"), } } } diff --git a/common/network-defaults/build.rs b/common/network-defaults/build.rs index aa52baa876..3ba632535d 100644 --- a/common/network-defaults/build.rs +++ b/common/network-defaults/build.rs @@ -31,7 +31,7 @@ fn main() { for var in variables_to_track { // if script fails, debug with `cargo check -vv`` - println!("Looking for {}", var); + println!("Looking for {var}"); // read pattern that looks like: // : &str = "" @@ -42,7 +42,7 @@ fn main() { .captures(source_of_truth) .and_then(|caps| caps.get(1).map(|match_| match_.as_str().to_string())) .expect("Couldn't find var in source file"); - println!("Storing {}={}", var, value); + println!("Storing {var}={value}"); replace_with.insert(var, value); } @@ -58,13 +58,11 @@ fn main() { // = let re = Regex::new(&pattern).unwrap(); contents = re - .replace(&contents, |_: ®ex::Captures| { - format!(r#"{}={}"#, var, value) - }) + .replace(&contents, |_: ®ex::Captures| format!(r#"{var}={value}"#)) .to_string(); } - println!("File contents to write:\n{}", contents); + println!("File contents to write:\n{contents}"); if output_path.exists() { fs::write(output_path, contents).unwrap(); } else { diff --git a/common/network-defaults/src/env_setup.rs b/common/network-defaults/src/env_setup.rs index 4ab07259a7..fb67056387 100644 --- a/common/network-defaults/src/env_setup.rs +++ b/common/network-defaults/src/env_setup.rs @@ -25,7 +25,7 @@ fn print_env_vars_with_keys_in_file + Copy>(config_env_file: P) { .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); + log::debug!("{key}: {val}"); } } diff --git a/common/network-defaults/src/network.rs b/common/network-defaults/src/network.rs index 8cebe53340..8e7b42543f 100644 --- a/common/network-defaults/src/network.rs +++ b/common/network-defaults/src/network.rs @@ -86,7 +86,7 @@ impl NymNetworkDetails { } } Err(VarError::NotPresent) => None, - err => panic!("Unable to set: {:?}", err), + err => panic!("Unable to set: {err:?}"), } } diff --git a/common/nym-metrics/src/lib.rs b/common/nym-metrics/src/lib.rs index dbe950e48a..8694ea73d1 100644 --- a/common/nym-metrics/src/lib.rs +++ b/common/nym-metrics/src/lib.rs @@ -344,9 +344,9 @@ impl fmt::Display for MetricsController { let metrics = self.gather(); let output = match String::from_utf8(metrics) { Ok(output) => output, - Err(e) => return write!(f, "Error decoding metrics to String: {}", e), + Err(e) => return write!(f, "Error decoding metrics to String: {e}"), }; - write!(f, "{}", output) + write!(f, "{output}") } } @@ -597,7 +597,7 @@ mod tests { assert_eq!(literal, "nym_metrics_foo"); let bar = "bar"; - let format = format!("foomp_{}", bar); + let format = format!("foomp_{bar}"); let formatted = prepend_package_name!(format); assert_eq!(formatted, "nym_metrics_foomp_bar"); } diff --git a/common/nym_offline_compact_ecash/src/scheme/setup.rs b/common/nym_offline_compact_ecash/src/scheme/setup.rs index dd469fb009..349706fe47 100644 --- a/common/nym_offline_compact_ecash/src/scheme/setup.rs +++ b/common/nym_offline_compact_ecash/src/scheme/setup.rs @@ -26,7 +26,7 @@ impl GroupParameters { pub fn new(attributes: usize) -> GroupParameters { assert!(attributes > 0); let gammas = (1..=attributes) - .map(|i| hash_g1(format!("gamma{}", i))) + .map(|i| hash_g1(format!("gamma{i}"))) .collect(); let delta = hash_g1("delta"); diff --git a/common/nymsphinx/chunking/src/fragment.rs b/common/nymsphinx/chunking/src/fragment.rs index f0f76ff8df..68419cac56 100644 --- a/common/nymsphinx/chunking/src/fragment.rs +++ b/common/nymsphinx/chunking/src/fragment.rs @@ -72,11 +72,7 @@ pub struct FragmentIdentifier { impl fmt::Display for FragmentIdentifier { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "Fragment Identifier: id: {} position: {}", - self.set_id, self.fragment_position - ) + write!(f, "{} @ {}", self.set_id, self.fragment_position) } } diff --git a/common/nymsphinx/params/src/key_rotation.rs b/common/nymsphinx/params/src/key_rotation.rs index 7c6e6179af..2a1ba7135f 100644 --- a/common/nymsphinx/params/src/key_rotation.rs +++ b/common/nymsphinx/params/src/key_rotation.rs @@ -15,6 +15,24 @@ pub enum SphinxKeyRotation { EvenRotation = 2, } +impl SphinxKeyRotation { + pub fn from_key_rotation_id(rotation_id: u32) -> Self { + rotation_id.into() + } + + pub fn is_unknown(&self) -> bool { + matches!(self, SphinxKeyRotation::Unknown) + } + + pub fn is_even(&self) -> bool { + matches!(self, SphinxKeyRotation::EvenRotation) + } + + pub fn is_odd(&self) -> bool { + matches!(self, SphinxKeyRotation::OddRotation) + } +} + #[derive(Debug, Error)] #[error("{received} is not a valid encoding of a sphinx key rotation")] pub struct InvalidSphinxKeyRotation { diff --git a/common/socks5-client-core/src/lib.rs b/common/socks5-client-core/src/lib.rs index 9c112e7928..17140d39fc 100644 --- a/common/socks5-client-core/src/lib.rs +++ b/common/socks5-client-core/src/lib.rs @@ -197,7 +197,7 @@ where let res = tokio::select! { biased; message = receiver.next() => { - log::debug!("Received message: {:?}", message); + log::debug!("Received message: {message:?}"); match message { Some(Socks5ControlMessage::Stop) => { log::info!("Received stop message"); @@ -209,7 +209,7 @@ where Ok(()) } Some(msg) = shutdown.wait_for_error() => { - log::info!("Task error: {:?}", msg); + log::info!("Task error: {msg:?}"); Err(msg) } _ = tokio::signal::ctrl_c() => { diff --git a/common/socks5-client-core/src/socks/client.rs b/common/socks5-client-core/src/socks/client.rs index 572849d296..5d79d66380 100644 --- a/common/socks5-client-core/src/socks/client.rs +++ b/common/socks5-client-core/src/socks/client.rs @@ -579,7 +579,7 @@ impl SocksClient { ); // Get valid auth methods let methods = self.get_available_methods().await?; - trace!("methods: {:?}", methods); + trace!("methods: {methods:?}"); let mut response = [0u8; 2]; diff --git a/common/socks5-client-core/src/socks/mixnet_responses.rs b/common/socks5-client-core/src/socks/mixnet_responses.rs index 05cb5bd48d..f74681c8de 100644 --- a/common/socks5-client-core/src/socks/mixnet_responses.rs +++ b/common/socks5-client-core/src/socks/mixnet_responses.rs @@ -61,7 +61,7 @@ impl MixnetResponseListener { control_response: ControlResponse, ) -> Result<(), Socks5ClientCoreError> { error!("received a control response which we don't know how to handle yet!"); - error!("got: {:?}", control_response); + error!("got: {control_response:?}"); // I guess we'd need another channel here to forward those to where they need to go @@ -88,7 +88,7 @@ impl MixnetResponseListener { } Socks5ResponseContent::Query(response) => { error!("received a query response which we don't know how to handle yet!"); - error!("got: {:?}", response); + error!("got: {response:?}"); // I guess we'd need another channel here to forward those to where they need to go diff --git a/common/socks5/proxy-helpers/src/proxy_runner/inbound.rs b/common/socks5/proxy-helpers/src/proxy_runner/inbound.rs index e63df37851..6cdf0efa9c 100644 --- a/common/socks5/proxy-helpers/src/proxy_runner/inbound.rs +++ b/common/socks5/proxy-helpers/src/proxy_runner/inbound.rs @@ -122,8 +122,7 @@ where biased; _ = &mut shutdown_future => { debug!( - "closing inbound proxy after outbound was closed {:?} ago", - SHUTDOWN_TIMEOUT + "closing inbound proxy after outbound was closed {SHUTDOWN_TIMEOUT:?} ago" ); // inform remote just in case it was closed because of lack of heartbeat. // worst case the remote will just have couple of false negatives @@ -169,7 +168,7 @@ where } } } - trace!("{} - inbound closed", connection_id); + trace!("{connection_id} - inbound closed"); shutdown_notify.notify_one(); shutdown_listener.disarm(); diff --git a/common/socks5/proxy-helpers/src/proxy_runner/outbound.rs b/common/socks5/proxy-helpers/src/proxy_runner/outbound.rs index 63d49fb311..dddae8d894 100644 --- a/common/socks5/proxy-helpers/src/proxy_runner/outbound.rs +++ b/common/socks5/proxy-helpers/src/proxy_runner/outbound.rs @@ -72,12 +72,12 @@ pub(super) async fn run_outbound( } } _ = &mut mix_timeout => { - warn!("didn't get anything from the client on {} mixnet in {:?}. Shutting down the proxy.", connection_id, MIX_TTL); + warn!("didn't get anything from the client on {connection_id} mixnet in {MIX_TTL:?}. Shutting down the proxy."); // If they were online it's kinda their fault they didn't send any heartbeat messages. break; } _ = &mut shutdown_future => { - debug!("closing outbound proxy after inbound was closed {:?} ago", SHUTDOWN_TIMEOUT); + debug!("closing outbound proxy after inbound was closed {SHUTDOWN_TIMEOUT:?} ago"); break; } _ = shutdown_listener.recv() => { @@ -87,7 +87,7 @@ pub(super) async fn run_outbound( } } - trace!("{} - outbound closed", connection_id); + trace!("{connection_id} - outbound closed"); shutdown_notify.notify_one(); shutdown_listener.disarm(); diff --git a/common/socks5/requests/src/request.rs b/common/socks5/requests/src/request.rs index 28a5182d00..32d9b4e9e2 100644 --- a/common/socks5/requests/src/request.rs +++ b/common/socks5/requests/src/request.rs @@ -360,7 +360,7 @@ impl Socks5RequestContent { let query_bytes: Vec = make_bincode_serializer() .serialize(&query) .tap_err(|err| { - log::error!("Failed to serialize query request: {:?}: {err}", query); + log::error!("Failed to serialize query request: {query:?}: {err}"); }) .unwrap_or_default(); std::iter::once(RequestFlag::Query as u8) diff --git a/common/socks5/requests/src/response.rs b/common/socks5/requests/src/response.rs index 584f39df5b..08e45c1de9 100644 --- a/common/socks5/requests/src/response.rs +++ b/common/socks5/requests/src/response.rs @@ -213,7 +213,7 @@ impl Socks5ResponseContent { let query_bytes: Vec = make_bincode_serializer() .serialize(&query) .tap_err(|err| { - log::error!("Failed to serialize query response: {:?}: {err}", query); + log::error!("Failed to serialize query response: {query:?}: {err}"); }) .unwrap_or_default(); std::iter::once(ResponseFlag::Query as u8) diff --git a/common/statistics/src/clients/gateway_conn_statistics.rs b/common/statistics/src/clients/gateway_conn_statistics.rs index 961c7a0f7f..bb42c676d5 100644 --- a/common/statistics/src/clients/gateway_conn_statistics.rs +++ b/common/statistics/src/clients/gateway_conn_statistics.rs @@ -77,7 +77,7 @@ impl GatewayStatsControl { fn report_counters(&self) { log::trace!("packet statistics: {:?}", &self.stats); let (summary_sent, summary_recv) = self.stats.summary(); - log::debug!("{}", summary_sent); - log::debug!("{}", summary_recv); + log::debug!("{summary_sent}"); + log::debug!("{summary_recv}"); } } diff --git a/common/statistics/src/clients/nym_api_statistics.rs b/common/statistics/src/clients/nym_api_statistics.rs index 9c3ee609c0..ddba38c0a8 100644 --- a/common/statistics/src/clients/nym_api_statistics.rs +++ b/common/statistics/src/clients/nym_api_statistics.rs @@ -77,7 +77,7 @@ impl NymApiStatsControl { fn report_counters(&self) { log::trace!("packet statistics: {:?}", &self.stats); let (summary_sent, summary_recv) = self.stats.summary(); - log::debug!("{}", summary_sent); - log::debug!("{}", summary_recv); + log::debug!("{summary_sent}"); + log::debug!("{summary_recv}"); } } diff --git a/common/statistics/src/clients/packet_statistics.rs b/common/statistics/src/clients/packet_statistics.rs index 866215c751..b2c6f56d5c 100644 --- a/common/statistics/src/clients/packet_statistics.rs +++ b/common/statistics/src/clients/packet_statistics.rs @@ -529,8 +529,8 @@ impl PacketStatisticsControl { fn report_counters(&self) { log::trace!("packet statistics: {:?}", &self.stats); let (summary_sent, summary_recv) = self.stats.summary(); - log::debug!("{}", summary_sent); - log::debug!("{}", summary_recv); + log::debug!("{summary_sent}"); + log::debug!("{summary_recv}"); } fn check_for_notable_events(&self) { diff --git a/common/statistics/src/lib.rs b/common/statistics/src/lib.rs index 6eb1eea7ff..ad105c9f45 100644 --- a/common/statistics/src/lib.rs +++ b/common/statistics/src/lib.rs @@ -41,7 +41,7 @@ fn generate_stats_id>(prefix: &str, id_seed: M) -> String { hasher.update(prefix); hasher.update(&id_seed); let output = hasher.finalize(); - format!("{:x}", output) + format!("{output:x}") } pub fn hash_identifier>(identifier: M) -> String { diff --git a/common/task/src/connections.rs b/common/task/src/connections.rs index 8557b84676..35f448c622 100644 --- a/common/task/src/connections.rs +++ b/common/task/src/connections.rs @@ -94,7 +94,7 @@ impl LaneQueueLengths { log::warn!("Timeout reached while waiting for queue to clear"); break; } - log::trace!("Waiting for queue to clear ({} items left)", lane_length); + log::trace!("Waiting for queue to clear ({lane_length} items left)"); tokio::time::sleep(Duration::from_millis(100)).await; } } diff --git a/common/task/src/manager.rs b/common/task/src/manager.rs index ba605f5a55..d5af78fcc0 100644 --- a/common/task/src/manager.rs +++ b/common/task/src/manager.rs @@ -163,7 +163,7 @@ impl TaskManager { // Announce that we are operational. This means that in the application where this is used, // everything is up and running and ready to go. if let Err(msg) = sender.send(Box::new(start_status)).await { - log::error!("Error sending status message: {}", msg); + log::error!("Error sending status message: {msg}"); }; if let Some(mut task_status_rx) = self.task_status_rx.take() { diff --git a/common/task/src/signal.rs b/common/task/src/signal.rs index 483c7c630b..ebaab7b20f 100644 --- a/common/task/src/signal.rs +++ b/common/task/src/signal.rs @@ -49,7 +49,7 @@ pub async fn wait_for_signal_and_error(shutdown: &mut TaskManager) -> Result<(), Ok(()) } Some(msg) = shutdown.wait_for_error() => { - log::info!("Task error: {:?}", msg); + log::info!("Task error: {msg:?}"); Err(msg) } } @@ -63,7 +63,7 @@ pub async fn wait_for_signal_and_error(shutdown: &mut TaskManager) -> Result<(), Ok(()) }, Some(msg) = shutdown.wait_for_error() => { - log::info!("Task error: {:?}", msg); + log::info!("Task error: {msg:?}"); Err(msg) } } diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index ba34832b04..64d60171f4 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -17,6 +17,7 @@ rand = { workspace = true } reqwest = { workspace = true, features = ["json"] } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } +time = { workspace = true, features = ["serde"] } # 'serde' feature serde_json = { workspace = true, optional = true } @@ -26,7 +27,6 @@ tsify = { workspace = true, features = ["js"], optional = true } wasm-bindgen = { workspace = true, optional = true } ## internal -nym-config = { path = "../config" } nym-crypto = { path = "../crypto" } nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } nym-sphinx-addressing = { path = "../nymsphinx/addressing" } @@ -34,7 +34,6 @@ nym-sphinx-types = { path = "../nymsphinx/types", features = [ "sphinx", "outfox", ] } -nym-sphinx-routing = { path = "../nymsphinx/routing" } # I'm not sure how to feel about pulling in this dependency here... diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 53886ed5fd..b6d95e356e 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -13,6 +13,7 @@ use std::borrow::Borrow; use std::collections::{HashMap, HashSet}; use std::fmt::Display; use std::net::IpAddr; +use time::OffsetDateTime; use tracing::{debug, trace, warn}; pub use crate::node::{EntryDetails, RoutingNode, SupportedRoles}; @@ -95,17 +96,25 @@ pub type MixLayer = u8; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct NymTopologyMetadata { - key_rotation_id: u32, + pub key_rotation_id: u32, // we have to keep track of key rotation id anyway, so we might as well also include the epoch id // to keep track of the data staleness - absolute_epoch_id: EpochId, + pub absolute_epoch_id: EpochId, + + #[serde(with = "time::serde::rfc3339")] + pub refreshed_at: OffsetDateTime, } impl NymTopologyMetadata { - pub fn new(key_rotation_id: u32, absolute_epoch_id: EpochId) -> Self { + pub fn new( + key_rotation_id: u32, + absolute_epoch_id: EpochId, + refreshed_at: impl Into, + ) -> Self { NymTopologyMetadata { key_rotation_id, absolute_epoch_id, + refreshed_at: refreshed_at.into(), } } } @@ -116,6 +125,7 @@ impl Default for NymTopologyMetadata { NymTopologyMetadata { key_rotation_id: u32::MAX, absolute_epoch_id: 0, + refreshed_at: OffsetDateTime::now_utc(), } } } @@ -169,6 +179,10 @@ impl NymRouteProvider { self.topology.metadata.absolute_epoch_id } + pub fn metadata(&self) -> NymTopologyMetadata { + self.topology.metadata + } + pub fn new_empty(ignore_egress_epoch_roles: bool) -> NymRouteProvider { let this: Self = NymTopology::default().into(); this.with_ignore_egress_epoch_roles(ignore_egress_epoch_roles) diff --git a/common/topology/src/provider_trait.rs b/common/topology/src/provider_trait.rs index ad8381fa7d..b0b9126525 100644 --- a/common/topology/src/provider_trait.rs +++ b/common/topology/src/provider_trait.rs @@ -1,8 +1,9 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::NymTopology; +use crate::{NymTopology, NymTopologyMetadata}; pub use async_trait::async_trait; +use nym_api_requests::nym_nodes::NodesResponseMetadata; // hehe, wasm #[cfg(not(target_arch = "wasm32"))] @@ -47,3 +48,15 @@ impl TopologyProvider for HardcodedTopologyProvider { Some(self.topology.clone()) } } + +// helper trait to convert between nym-api response and the topology metadata +// (we don't want to be importing any of those in the other crates) +pub trait ToTopologyMetadata { + fn to_topology_metadata(&self) -> NymTopologyMetadata; +} + +impl ToTopologyMetadata for NodesResponseMetadata { + fn to_topology_metadata(&self) -> NymTopologyMetadata { + NymTopologyMetadata::new(self.rotation_id, self.absolute_epoch_id, self.refreshed_at) + } +} diff --git a/common/tun/src/linux/tun_device.rs b/common/tun/src/linux/tun_device.rs index ab3bbd71b7..936a141b3f 100644 --- a/common/tun/src/linux/tun_device.rs +++ b/common/tun/src/linux/tun_device.rs @@ -157,7 +157,7 @@ impl TunDevice { "-6", "addr", "add", - &format!("{}/{}", ipv6, netmaskv6), + &format!("{ipv6}/{netmaskv6}"), "dev", (tun.name()), ]) diff --git a/common/wasm/client-core/src/config/mod.rs b/common/wasm/client-core/src/config/mod.rs index 2378b20ba4..c7eb706180 100644 --- a/common/wasm/client-core/src/config/mod.rs +++ b/common/wasm/client-core/src/config/mod.rs @@ -505,9 +505,9 @@ pub struct ReplySurbsWasm { /// deciding it's never going to get them and would drop all pending messages pub maximum_reply_surb_drop_waiting_period_ms: u32, - /// Defines maximum amount of time given reply surb is going to be valid for. - /// This is going to be superseded by key rotation once implemented. - pub maximum_reply_surb_age_ms: u32, + /// Defines maximum number of times the client is going to re-request reply surbs + /// for clearing pending messages before giving up after making no progress. + pub maximum_reply_surbs_rerequests: usize, /// Defines maximum amount of time given reply key is going to be valid for. /// This is going to be superseded by key rotation once implemented. @@ -516,9 +516,6 @@ pub struct ReplySurbsWasm { /// Defines how many mix nodes the reply surb should go through. /// If not set, the default value is going to be used. pub surb_mix_hops: Option, - - /// Specifies if we should reset all the sender tags on startup - pub fresh_sender_tags: bool, } impl Default for ReplySurbsWasm { @@ -543,14 +540,11 @@ impl From for ConfigReplySurbs { maximum_reply_surb_drop_waiting_period: Duration::from_millis( reply_surbs.maximum_reply_surb_drop_waiting_period_ms as u64, ), - maximum_reply_surb_age: Duration::from_millis( - reply_surbs.maximum_reply_surb_age_ms as u64, - ), + maximum_reply_surbs_rerequests: reply_surbs.maximum_reply_surbs_rerequests, maximum_reply_key_age: Duration::from_millis( reply_surbs.maximum_reply_key_age_ms as u64, ), surb_mix_hops: reply_surbs.surb_mix_hops, - fresh_sender_tags: reply_surbs.fresh_sender_tags, } } } @@ -571,10 +565,9 @@ impl From for ReplySurbsWasm { maximum_reply_surb_drop_waiting_period_ms: reply_surbs .maximum_reply_surb_drop_waiting_period .as_millis() as u32, - maximum_reply_surb_age_ms: reply_surbs.maximum_reply_surb_age.as_millis() as u32, + maximum_reply_surbs_rerequests: reply_surbs.maximum_reply_surbs_rerequests, maximum_reply_key_age_ms: reply_surbs.maximum_reply_key_age.as_millis() as u32, surb_mix_hops: reply_surbs.surb_mix_hops, - fresh_sender_tags: reply_surbs.fresh_sender_tags, } } } diff --git a/common/wasm/client-core/src/config/override.rs b/common/wasm/client-core/src/config/override.rs index 859c28fd3a..1ee58f2437 100644 --- a/common/wasm/client-core/src/config/override.rs +++ b/common/wasm/client-core/src/config/override.rs @@ -383,16 +383,16 @@ pub struct ReplySurbsWasmOverride { #[tsify(optional)] pub maximum_reply_surb_drop_waiting_period_ms: Option, - /// Defines maximum amount of time given reply surb is going to be valid for. - /// This is going to be superseded by key rotation once implemented. - #[tsify(optional)] - pub maximum_reply_surb_age_ms: Option, - /// Defines maximum amount of time given reply key is going to be valid for. /// This is going to be superseded by key rotation once implemented. #[tsify(optional)] pub maximum_reply_key_age_ms: Option, + /// Defines maximum number of times the client is going to re-request reply surbs + /// for clearing pending messages before giving up after making no progress. + #[tsify(optional)] + pub maximum_reply_surbs_rerequests: Option, + #[tsify(optional)] pub surb_mix_hops: Option, @@ -429,14 +429,13 @@ impl From for ReplySurbsWasm { maximum_reply_surb_drop_waiting_period_ms: value .maximum_reply_surb_drop_waiting_period_ms .unwrap_or(def.maximum_reply_surb_drop_waiting_period_ms), - maximum_reply_surb_age_ms: value - .maximum_reply_surb_age_ms - .unwrap_or(def.maximum_reply_surb_age_ms), maximum_reply_key_age_ms: value .maximum_reply_key_age_ms .unwrap_or(def.maximum_reply_key_age_ms), surb_mix_hops: value.surb_mix_hops, - fresh_sender_tags: value.fresh_sender_tags, + maximum_reply_surbs_rerequests: value + .maximum_reply_surbs_rerequests + .unwrap_or(def.maximum_reply_surbs_rerequests), } } } diff --git a/common/wasm/client-core/src/helpers.rs b/common/wasm/client-core/src/helpers.rs index 17958b58ab..4cb5f06c55 100644 --- a/common/wasm/client-core/src/helpers.rs +++ b/common/wasm/client-core/src/helpers.rs @@ -19,7 +19,7 @@ use nym_client_core::init::{ use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_topology::wasm_helpers::WasmFriendlyNymTopology; -use nym_topology::{NymTopology, NymTopologyMetadata, RoutingNode}; +use nym_topology::{NymTopology, RoutingNode}; use nym_validator_client::client::IdentityKey; use nym_validator_client::{NymApiClient, UserAgent}; use rand::thread_rng; @@ -29,6 +29,7 @@ use wasm_bindgen_futures::future_to_promise; use wasm_utils::error::PromisableResult; pub use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage; +use nym_topology::provider_trait::ToTopologyMetadata; use wasm_utils::{console_log, console_warn}; // don't get too excited about the name, under the hood it's just a big fat placeholder @@ -82,20 +83,16 @@ pub async fn current_network_topology_async( let gateways_res = api_client .get_all_basic_entry_assigned_nodes_with_metadata() .await?; - if gateways_res.metadata != metadata { + if !gateways_res.metadata.consistency_check(&metadata) { console_warn!("inconsistent nodes metadata between mixnodes and gateways calls! {metadata:?} and {:?}", gateways_res.metadata); return Err(WasmCoreError::UnavailableNetworkTopology); } let gateways = gateways_res.nodes; - let topology = NymTopology::new( - NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id), - rewarded_set, - Vec::new(), - ) - .with_skimmed_nodes(&mixnodes) - .with_skimmed_nodes(&gateways); + let topology = NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new()) + .with_skimmed_nodes(&mixnodes) + .with_skimmed_nodes(&gateways); Ok(topology.into()) } diff --git a/common/wireguard/src/lib.rs b/common/wireguard/src/lib.rs index 3858d476bc..6b83c648d2 100644 --- a/common/wireguard/src/lib.rs +++ b/common/wireguard/src/lib.rs @@ -38,7 +38,7 @@ impl Drop for WgApiWrapper { fn drop(&mut self) { if let Err(e) = defguard_wireguard_rs::WireguardInterfaceApi::remove_interface(&self.inner) { - log::error!("Could not remove the wireguard interface: {:?}", e); + log::error!("Could not remove the wireguard interface: {e:?}"); } } } diff --git a/documentation/autodoc/src/main.rs b/documentation/autodoc/src/main.rs index 7f9513097c..812677fb99 100644 --- a/documentation/autodoc/src/main.rs +++ b/documentation/autodoc/src/main.rs @@ -163,7 +163,7 @@ fn main() -> io::Result<()> { write_output_to_file(&mut file, output)?; for (subcommand, subsubcommands) in subcommands { - writeln!(file, "\n## `{}` ", subcommand)?; + writeln!(file, "\n## `{subcommand}` ")?; let output = Command::new(main_command) .arg(subcommand) .arg("--help") @@ -195,12 +195,12 @@ fn execute_command_own_file(main_command: &str, subcommand: &str) -> io::Result< || get_last_word_from_filepath(main_command).unwrap() == "nym-node") && subcommand == "run" { - info!("SKIPPING {} {}", main_command, subcommand); + info!("SKIPPING {main_command} {subcommand}"); } else { let last_word = get_last_word_from_filepath(main_command); let output = Command::new(main_command).arg(subcommand).output()?; if !output.stdout.is_empty() { - info!("creating own file for {} {}", main_command, subcommand,); + info!("creating own file for {main_command} {subcommand}",); if !fs::metadata(WRITE_PATH) .map(|metadata| metadata.is_dir()) .unwrap_or(false) @@ -216,10 +216,7 @@ fn execute_command_own_file(main_command: &str, subcommand: &str) -> io::Result< write_output_to_file(&mut file, output)?; // execute help - info!( - "creating own file for {} {} --help", - main_command, subcommand, - ); + info!("creating own file for {main_command} {subcommand} --help",); if !fs::metadata(COMMAND_PATH) .map(|metadata| metadata.is_dir()) .unwrap_or(false) @@ -243,10 +240,7 @@ fn execute_command_own_file(main_command: &str, subcommand: &str) -> io::Result< debug!("empty stdout - nothing to write"); } } else { - info!( - "creating own file for {} {} --help", - main_command, subcommand, - ); + info!("creating own file for {main_command} {subcommand} --help",); if !fs::metadata(COMMAND_PATH) .map(|metadata| metadata.is_dir()) .unwrap_or(false) @@ -281,7 +275,7 @@ fn execute_command( if subsubcommand.is_some() { writeln!(file, "\n## `{} {}`", subcommand, subsubcommand.unwrap())?; - info!("executing {} {} --help ", main_command, subcommand); + info!("executing {main_command} {subcommand} --help "); let output = Command::new(main_command) .arg(subcommand) .arg(subsubcommand.unwrap()) @@ -294,7 +288,7 @@ fn execute_command( } // just subcommands } else { - writeln!(file, "\n## `{}`", subcommand)?; + writeln!(file, "\n## `{subcommand}`")?; // execute help let output = Command::new(main_command) @@ -318,9 +312,9 @@ fn execute_command( || get_last_word_from_filepath(main_command).unwrap() == "nymvisor" && subcommand == "run" { - info!("SKIPPING {} {}", main_command, subcommand); + info!("SKIPPING {main_command} {subcommand}"); } else { - info!("executing {} {}", main_command, subcommand); + info!("executing {main_command} {subcommand}"); let output = Command::new(main_command).arg(subcommand).output()?; if !output.stdout.is_empty() { writeln!(file, "Example output:")?; diff --git a/nym-api/build.rs b/nym-api/build.rs index 10307c5dfd..6bc476b72f 100644 --- a/nym-api/build.rs +++ b/nym-api/build.rs @@ -9,14 +9,14 @@ const SQLITE_DB_FILENAME: &str = "nym-api-example.sqlite"; #[tokio::main] async fn main() { let out_dir = env::var("OUT_DIR").unwrap(); - let database_path = format!("{}/{}", out_dir, SQLITE_DB_FILENAME); + let database_path = format!("{out_dir}/{SQLITE_DB_FILENAME}"); #[cfg(target_family = "unix")] write_db_path_to_file(&out_dir, SQLITE_DB_FILENAME) .await .ok(); - let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path)) + let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc")) .await .expect("Failed to create SQLx database connection"); @@ -87,8 +87,7 @@ async fn write_db_path_to_file(out_dir: &str, db_filename: &str) -> anyhow::Resu let mut file = File::create("enter_db.sh").await?; let contents = format!( "#!/bin/sh\n\ - sqlite3 -init settings.sql {}/{}", - out_dir, db_filename, + sqlite3 -init settings.sql {out_dir}/{db_filename}", ); file.write_all(contents.as_bytes()).await?; diff --git a/nym-api/nym-api-requests/src/models.rs b/nym-api/nym-api-requests/src/models.rs index a9c5c2ccd4..117fdcc07d 100644 --- a/nym-api/nym-api-requests/src/models.rs +++ b/nym-api/nym-api-requests/src/models.rs @@ -18,9 +18,7 @@ use nym_crypto::asymmetric::x25519::{self, serde_helpers::bs58_x25519_pubkey}; use nym_mixnet_contract_common::nym_node::Role; use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams}; use nym_mixnet_contract_common::rewarding::RewardEstimate; -use nym_mixnet_contract_common::{ - EpochId, GatewayBond, IdentityKey, Interval, MixNode, NodeId, Percent, -}; +use nym_mixnet_contract_common::{GatewayBond, IdentityKey, Interval, MixNode, NodeId, Percent}; use nym_network_defaults::{DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT}; use nym_node_requests::api::v1::authenticator::models::Authenticator; use nym_node_requests::api::v1::gateway::models::Wireguard; @@ -42,7 +40,7 @@ use time::{Date, OffsetDateTime}; use tracing::{error, warn}; use utoipa::{IntoParams, ToResponse, ToSchema}; -pub use nym_mixnet_contract_common::KeyRotationState; +pub use nym_mixnet_contract_common::{EpochId, KeyRotationId, KeyRotationState}; pub use nym_node_requests::api::v1::node::models::BinaryBuildInformationOwned; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] @@ -1479,8 +1477,43 @@ impl NodeRefreshBody { } } -#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)] pub struct KeyRotationInfoResponse { + #[serde(flatten)] + pub details: KeyRotationDetails, + + // helper field that holds calculated data based on the `details` field + // this is to expose the information in a format more easily accessible by humans + // without having to do any calculations + pub progress: KeyRotationProgressInfo, +} + +impl From for KeyRotationInfoResponse { + fn from(details: KeyRotationDetails) -> Self { + KeyRotationInfoResponse { + details, + progress: KeyRotationProgressInfo { + current_key_rotation_id: details.current_key_rotation_id(), + current_rotation_starting_epoch: details.current_rotation_starting_epoch_id(), + current_rotation_ending_epoch: details.current_rotation_starting_epoch_id() + + details.key_rotation_state.validity_epochs + - 1, + }, + } + } +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)] +pub struct KeyRotationProgressInfo { + pub current_key_rotation_id: u32, + + pub current_rotation_starting_epoch: u32, + + pub current_rotation_ending_epoch: u32, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)] +pub struct KeyRotationDetails { pub key_rotation_state: KeyRotationState, #[schema(value_type = u32)] @@ -1494,7 +1527,7 @@ pub struct KeyRotationInfoResponse { pub epoch_duration: Duration, } -impl KeyRotationInfoResponse { +impl KeyRotationDetails { pub fn current_key_rotation_id(&self) -> u32 { self.key_rotation_state .key_rotation_id(self.current_absolute_epoch_id) @@ -1534,7 +1567,7 @@ impl KeyRotationInfoResponse { } // based on the current **TIME**, determine what's the expected current rotation id - pub fn expected_current_rotation_id(&self) -> u32 { + pub fn expected_current_rotation_id(&self) -> KeyRotationId { let now = OffsetDateTime::now_utc(); let current_end = now + self.epoch_duration; if now < current_end { diff --git a/nym-api/nym-api-requests/src/nym_nodes.rs b/nym-api/nym-api-requests/src/nym_nodes.rs index 6a19eb76d9..ca7ffab7e8 100644 --- a/nym-api/nym-api-requests/src/nym_nodes.rs +++ b/nym-api/nym-api-requests/src/nym_nodes.rs @@ -70,9 +70,7 @@ impl CachedNodesResponse { } } -#[derive( - Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema, PartialEq, -)] +#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)] pub struct NodesResponseMetadata { pub status: Option, #[schema(value_type = u32)] @@ -82,6 +80,12 @@ pub struct NodesResponseMetadata { } impl NodesResponseMetadata { + pub fn consistency_check(&self, other: &NodesResponseMetadata) -> bool { + self.status == other.status + && self.absolute_epoch_id == other.absolute_epoch_id + && self.rotation_id == other.rotation_id + } + pub fn refreshed_at(&self) -> OffsetDateTime { self.refreshed_at.into() } diff --git a/nym-api/src/ecash/dkg/state/serde_helpers.rs b/nym-api/src/ecash/dkg/state/serde_helpers.rs index 8057ef5d20..a19141e443 100644 --- a/nym-api/src/ecash/dkg/state/serde_helpers.rs +++ b/nym-api/src/ecash/dkg/state/serde_helpers.rs @@ -19,7 +19,7 @@ pub(super) mod bte_pk_serde { { let vec: Vec = Deserialize::deserialize(deserializer)?; PublicKeyWithProof::try_from_bytes(&vec) - .map_err(|err| Error::custom(format_args!("{:?}", err))) + .map_err(|err| Error::custom(format_args!("{err:?}"))) .map(Box::new) } } @@ -55,7 +55,7 @@ pub(super) mod recovered_keys { .into_iter() .map(|(idx, rec)| { RecoveredVerificationKeys::try_from_bytes(&rec) - .map_err(|err| D::Error::custom(format_args!("{:?}", err))) + .map_err(|err| D::Error::custom(format_args!("{err:?}"))) .map(|vk| (idx, vk)) }) .collect() diff --git a/nym-api/src/epoch_operations/helpers.rs b/nym-api/src/epoch_operations/helpers.rs index d21fd1aadb..f28021058c 100644 --- a/nym-api/src/epoch_operations/helpers.rs +++ b/nym-api/src/epoch_operations/helpers.rs @@ -171,9 +171,9 @@ mod tests { }; if a > b { - assert!(a - b < epsilon, "{} != {}", a, b) + assert!(a - b < epsilon, "{a} != {b}") } else { - assert!(b - a < epsilon, "{} != {}", a, b) + assert!(b - a < epsilon, "{a} != {b}") } } diff --git a/nym-api/src/network_monitor/test_route/mod.rs b/nym-api/src/network_monitor/test_route/mod.rs index 6280ba9f01..7e31aaba62 100644 --- a/nym-api/src/network_monitor/test_route/mod.rs +++ b/nym-api/src/network_monitor/test_route/mod.rs @@ -9,6 +9,7 @@ use nym_mixnet_contract_common::{EpochId, EpochRewardedSet, RewardedSet}; use nym_topology::node::RoutingNode; use nym_topology::{NymRouteProvider, NymTopology, NymTopologyMetadata}; use std::fmt::{Debug, Formatter}; +use time::OffsetDateTime; #[derive(Clone)] pub(crate) struct TestRoute { @@ -42,7 +43,7 @@ impl TestRoute { TestRoute { id, nodes: NymTopology::new( - NymTopologyMetadata::new(key_rotation_id, 0), + NymTopologyMetadata::new(key_rotation_id, 0, OffsetDateTime::now_utc()), fake_rewarded_set, nodes, ), diff --git a/nym-api/src/nym_contract_cache/handlers.rs b/nym-api/src/nym_contract_cache/handlers.rs index 7519b222d6..e72182701b 100644 --- a/nym-api/src/nym_contract_cache/handlers.rs +++ b/nym-api/src/nym_contract_cache/handlers.rs @@ -11,7 +11,7 @@ use crate::support::legacy_helpers::{to_legacy_gateway, to_legacy_mixnode}; use axum::extract::{Query, State}; use axum::Router; use nym_api_requests::legacy::LegacyMixNodeDetailsWithLayer; -use nym_api_requests::models::{KeyRotationInfoResponse, MixNodeBondAnnotated}; +use nym_api_requests::models::{KeyRotationDetails, KeyRotationInfoResponse, MixNodeBondAnnotated}; use nym_http_api_common::{FormattedResponse, OutputParams}; use nym_mixnet_contract_common::reward_params::Performance; use nym_mixnet_contract_common::{reward_params::RewardingParams, GatewayBond, Interval, NodeId}; @@ -511,10 +511,12 @@ async fn get_current_key_rotation_info( let current_interval = contract_cache.current_interval().await?; let key_rotation_state = contract_cache.get_key_rotation_state().await?; - Ok(output.to_response(KeyRotationInfoResponse { + let details = KeyRotationDetails { key_rotation_state, current_absolute_epoch_id: current_interval.current_epoch_absolute_id(), current_epoch_start: current_interval.current_epoch_start(), epoch_duration: current_interval.epoch_length(), - })) + }; + + Ok(output.to_response(details.into())) } diff --git a/nym-api/tests/public-api/network.rs b/nym-api/tests/public-api/network.rs index 62f2440011..b6e1e3bc4c 100644 --- a/nym-api/tests/public-api/network.rs +++ b/nym-api/tests/public-api/network.rs @@ -7,7 +7,7 @@ async fn test_get_chain_status() -> Result<(), String> { .get(&url) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; let json = validate_json_response(res).await?; let block_header = json @@ -35,7 +35,7 @@ async fn test_get_network_details() -> Result<(), String> { .get(&url) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; let json = validate_json_response(res).await?; assert!( @@ -60,7 +60,7 @@ async fn test_get_nym_contracts() -> Result<(), String> { .get(&url) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; let json = validate_json_response(res).await?; assert!( @@ -81,7 +81,7 @@ async fn test_get_nym_contracts_detailed() -> Result<(), String> { .get(&url) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; let json = validate_json_response(res).await?; let mixnet_contract = json diff --git a/nym-api/tests/public-api/nym_nodes.rs b/nym-api/tests/public-api/nym_nodes.rs index 63ff057d45..dda646b3b8 100644 --- a/nym-api/tests/public-api/nym_nodes.rs +++ b/nym-api/tests/public-api/nym_nodes.rs @@ -90,7 +90,7 @@ async fn test_get_historical_performance() -> Result<(), String> { .query(&[("date", date)]) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; let json = validate_json_response(res).await?; assert!( diff --git a/nym-api/tests/public-api/unstable_status.rs b/nym-api/tests/public-api/unstable_status.rs index fe36329646..52a0084480 100644 --- a/nym-api/tests/public-api/unstable_status.rs +++ b/nym-api/tests/public-api/unstable_status.rs @@ -87,7 +87,7 @@ async fn test_get_latest_network_monitor_run_details() -> Result<(), String> { .get(&follow_up_url) .send() .await - .map_err(|err| format!("Failed to follow up with URL {}: {}", follow_up_url, err))?; + .map_err(|err| format!("Failed to follow up with URL {follow_up_url}: {err}"))?; assert!(follow_up_res.status().is_success()); Ok(()) } diff --git a/nym-api/tests/public-api/utils.rs b/nym-api/tests/public-api/utils.rs index 96c8c3b61e..ad0f61f653 100644 --- a/nym-api/tests/public-api/utils.rs +++ b/nym-api/tests/public-api/utils.rs @@ -25,7 +25,7 @@ pub async fn make_request(url: &str) -> Result { .get(url) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; if res.status().is_success() { Ok(res) @@ -43,7 +43,7 @@ pub async fn validate_json_response(res: Response) -> Result { res.json::() .await - .map_err(|err| format!("Invalid JSON response: {}", err)) + .map_err(|err| format!("Invalid JSON response: {err}")) } #[allow(dead_code)] @@ -54,11 +54,11 @@ pub async fn get_any_node_id() -> Result { .get(&url) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; let json: Value = res .json() .await - .map_err(|err| format!("Failed to parse response as JSON: {}", err))?; + .map_err(|err| format!("Failed to parse response as JSON: {err}"))?; let id = json .get("data") @@ -80,11 +80,11 @@ pub async fn get_mixnode_node_id() -> Result { .get(&url) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; let json: Value = res .json() .await - .map_err(|err| format!("Failed to parse response as JSON: {}", err))?; + .map_err(|err| format!("Failed to parse response as JSON: {err}"))?; json.get("data") .and_then(|v| v.as_array()) @@ -110,11 +110,11 @@ pub async fn get_gateway_identity_key() -> Result { .get(&url) .send() .await - .map_err(|err| format!("Failed to send request to {}: {}", url, err))?; + .map_err(|err| format!("Failed to send request to {url}: {err}"))?; let json: Value = res .json() .await - .map_err(|err| format!("Failed to parse response as JSON: {}", err))?; + .map_err(|err| format!("Failed to parse response as JSON: {err}"))?; let key = json .get("data") diff --git a/nym-credential-proxy/nym-credential-proxy/src/storage/mod.rs b/nym-credential-proxy/nym-credential-proxy/src/storage/mod.rs index 5480f96d5b..1759ccdfc3 100644 --- a/nym-credential-proxy/nym-credential-proxy/src/storage/mod.rs +++ b/nym-credential-proxy/nym-credential-proxy/src/storage/mod.rs @@ -389,7 +389,7 @@ mod tests { let file = NamedTempFile::new()?; let path = file.into_temp_path(); - println!("Creating database at {:?}...", path); + println!("Creating database at {path:?}..."); Ok(StorageTestWrapper { inner: VpnApiStorage::init(&path).await?, @@ -455,11 +455,11 @@ mod tests { .insert_new_pending_async_shares_request(dummy_uuid, "1234", "1234") .await; if let Err(e) = &res { - println!("❌ {}", e); + println!("❌ {e}"); } assert!(res.is_ok()); let res = res.unwrap(); - println!("res = {:?}", res); + println!("res = {res:?}"); assert_eq!(res.status, BlindedSharesStatus::Pending); println!("🚀 update_pending_blinded_share_error..."); @@ -467,11 +467,11 @@ mod tests { .update_pending_async_blinded_shares_error(0, "1234", "1234", "this is an error") .await; if let Err(e) = &res { - println!("❌ {}", e); + println!("❌ {e}"); } assert!(res.is_ok()); let res = res.unwrap(); - println!("res = {:?}", res); + println!("res = {res:?}"); assert!(res.error_message.is_some()); assert_eq!(res.status, BlindedSharesStatus::Error); @@ -480,11 +480,11 @@ mod tests { .update_pending_async_blinded_shares_issued(42, "1234", "1234") .await; if let Err(e) = &res { - println!("❌ {}", e); + println!("❌ {e}"); } assert!(res.is_ok()); let res = res.unwrap(); - println!("res = {:?}", res); + println!("res = {res:?}"); assert_eq!(res.status, BlindedSharesStatus::Issued); assert!(res.error_message.is_none()); diff --git a/nym-network-monitor/src/accounting.rs b/nym-network-monitor/src/accounting.rs index 9132e582c0..caddfe1db6 100644 --- a/nym-network-monitor/src/accounting.rs +++ b/nym-network-monitor/src/accounting.rs @@ -430,7 +430,7 @@ async fn db_connection(database_url: Option<&String>) -> Result) -> anyhow::Result<( pub async fn submit_metrics(database_url: Option<&String>) -> anyhow::Result<()> { if let Err(e) = submit_metrics_to_db(database_url).await { - error!("Error submitting metrics to db: {}", e); + error!("Error submitting metrics to db: {e}"); } if let Some(private_key) = PRIVATE_KEY.get() { diff --git a/nym-network-monitor/src/http.rs b/nym-network-monitor/src/http.rs index 7255656344..6608345242 100644 --- a/nym-network-monitor/src/http.rs +++ b/nym-network-monitor/src/http.rs @@ -84,7 +84,7 @@ impl HttpServer { axum::serve(listener, app).with_graceful_shutdown(self.cancel.cancelled_owned()); info!("##########################################################################################"); - info!("######################### HTTP server running, with {} clients ############################################", n_clients); + info!("######################### HTTP server running, with {n_clients} clients ############################################"); info!("##########################################################################################"); server_future.await?; diff --git a/nym-network-monitor/src/main.rs b/nym-network-monitor/src/main.rs index 3442cfe7ea..d0d04d62c6 100644 --- a/nym-network-monitor/src/main.rs +++ b/nym-network-monitor/src/main.rs @@ -10,7 +10,8 @@ use nym_network_defaults::setup_env; use nym_network_defaults::var_names::NYM_API; use nym_sdk::mixnet::{self, MixnetClient}; use nym_sphinx::chunking::monitoring; -use nym_topology::{HardcodedTopologyProvider, NymTopology, NymTopologyMetadata}; +use nym_topology::provider_trait::ToTopologyMetadata; +use nym_topology::{HardcodedTopologyProvider, NymTopology}; use std::fs::File; use std::io::Write; use std::sync::LazyLock; @@ -26,7 +27,7 @@ use tokio::{signal::ctrl_c, sync::RwLock}; use tokio_util::sync::CancellationToken; static NYM_API_URL: LazyLock = LazyLock::new(|| { - std::env::var(NYM_API).unwrap_or_else(|_| panic!("{} env var not set", NYM_API)) + std::env::var(NYM_API).unwrap_or_else(|_| panic!("{NYM_API} env var not set")) }); static MIXNET_TIMEOUT: OnceCell = OnceCell::const_new(); @@ -48,10 +49,10 @@ async fn make_clients( ) { loop { let spawned_clients = clients.read().await.len(); - info!("Currently spawned clients: {}", spawned_clients); + info!("Currently spawned clients: {spawned_clients}"); // If we have enough clients, sleep for a minute and remove the oldest one if spawned_clients >= n_clients { - info!("New client will be spawned in {} seconds", lifetime); + info!("New client will be spawned in {lifetime} seconds"); tokio::time::sleep(tokio::time::Duration::from_secs(lifetime)).await; info!("Removing oldest client"); if let Some(dropped_client) = clients.write().await.pop_front() { @@ -74,7 +75,7 @@ async fn make_clients( let client = match make_client(topology.clone()).await { Ok(client) => client, Err(err) => { - warn!("{}, moving on", err); + warn!("{err}, moving on"); continue; } }; @@ -171,12 +172,10 @@ async fn nym_topology_from_env() -> anyhow::Result { let nodes = nodes_response.nodes; let metadata = nodes_response.metadata; - Ok(NymTopology::new( - NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id), - rewarded_set, - Vec::new(), + Ok( + NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new()) + .with_skimmed_nodes(&nodes), ) - .with_skimmed_nodes(&nodes)) } #[tokio::main] diff --git a/nym-node-status-api/nym-node-status-agent/src/main.rs b/nym-node-status-api/nym-node-status-agent/src/main.rs index d3078753fa..0415864d9f 100644 --- a/nym-node-status-api/nym-node-status-agent/src/main.rs +++ b/nym-node-status-api/nym-node-status-agent/src/main.rs @@ -51,7 +51,7 @@ pub(crate) fn setup_tracing() { "axum", ]; for crate_name in filter_crates { - filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))); + filter = filter.add_directive(directive_checked(format!("{crate_name}=warn"))); } filter = filter.add_directive(directive_checked("nym_bin_common=debug")); diff --git a/nym-node-status-api/nym-node-status-agent/src/probe.rs b/nym-node-status-api/nym-node-status-agent/src/probe.rs index 67e7e508c6..08d0a823aa 100644 --- a/nym-node-status-api/nym-node-status-agent/src/probe.rs +++ b/nym-node-status-api/nym-node-status-agent/src/probe.rs @@ -30,7 +30,7 @@ impl GwProbe { } Err(e) => { error!("Failed to stat binary at {}: {}", &self.path, e); - return format!("Failed to access binary: {}", e); + return format!("Failed to access binary: {e}"); } } } @@ -55,17 +55,17 @@ impl GwProbe { output.status.code().unwrap_or(-1), stderr ); - format!("Command failed: {}", stderr) + format!("Command failed: {stderr}") } } Err(e) => { error!("Failed to get command output: {}", e); - format!("Failed to get command output: {}", e) + format!("Failed to get command output: {e}") } }, Err(e) => { error!("Failed to spawn process: {}", e); - format!("Failed to spawn process: {}", e) + format!("Failed to spawn process: {e}") } } } diff --git a/nym-node-status-api/nym-node-status-api/build.rs b/nym-node-status-api/nym-node-status-api/build.rs index 1032fcb691..9da6d48d2a 100644 --- a/nym-node-status-api/nym-node-status-api/build.rs +++ b/nym-node-status-api/nym-node-status-api/build.rs @@ -13,7 +13,7 @@ const SQLITE_DB_FILENAME: &str = "nym-node-status-api.sqlite"; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { let out_dir = read_env_var("OUT_DIR")?; - let database_path = format!("{}/{}?mode=rwc", out_dir, SQLITE_DB_FILENAME); + let database_path = format!("{out_dir}/{SQLITE_DB_FILENAME}?mode=rwc"); write_db_path_to_file(&out_dir, SQLITE_DB_FILENAME).await?; let mut conn = SqliteConnection::connect(&database_path).await?; @@ -44,8 +44,7 @@ async fn write_db_path_to_file(out_dir: &str, db_filename: &str) -> anyhow::Resu let mut file = File::create("enter_db.sh").await?; let contents = format!( "#!/bin/bash\n\ - sqlite3 -init settings.sql {}/{}", - out_dir, db_filename, + sqlite3 -init settings.sql {out_dir}/{db_filename}", ); file.write_all(contents.as_bytes()).await?; diff --git a/nym-node-status-api/nym-node-status-api/src/http/error.rs b/nym-node-status-api/nym-node-status-api/src/http/error.rs index ce0e0f4060..0282958054 100644 --- a/nym-node-status-api/nym-node-status-api/src/http/error.rs +++ b/nym-node-status-api/nym-node-status-api/src/http/error.rs @@ -44,7 +44,7 @@ impl HttpError { pub(crate) fn no_delegations_for_node(node_id: NodeId) -> Self { Self { - message: format!("No delegation data for node_id={}", node_id), + message: format!("No delegation data for node_id={node_id}"), status: axum::http::StatusCode::NOT_FOUND, } } diff --git a/nym-node-status-api/nym-node-status-api/src/http/models.rs b/nym-node-status-api/nym-node-status-api/src/http/models.rs index 2ddd8e19df..d99da2c698 100644 --- a/nym-node-status-api/nym-node-status-api/src/http/models.rs +++ b/nym-node-status-api/nym-node-status-api/src/http/models.rs @@ -230,7 +230,7 @@ impl DVpnGateway { fn to_percent(performance: u8) -> String { let fraction = performance as f32 / 100.0; - format!("{:.2}", fraction) + format!("{fraction:.2}") } #[cfg(test)] diff --git a/nym-node-status-api/nym-node-status-api/src/http/server.rs b/nym-node-status-api/nym-node-status-api/src/http/server.rs index 36c55a787a..548fc6206d 100644 --- a/nym-node-status-api/nym-node-status-api/src/http/server.rs +++ b/nym-node-status-api/nym-node-status-api/src/http/server.rs @@ -35,7 +35,7 @@ pub(crate) async fn start_http_api( .await; let router = router_builder.with_state(state); - let bind_addr = format!("0.0.0.0:{}", http_port); + let bind_addr = format!("0.0.0.0:{http_port}"); tracing::info!("Binding server to {bind_addr}"); let server = router.build_server(bind_addr).await?; diff --git a/nym-node-status-api/nym-node-status-api/src/logging.rs b/nym-node-status-api/nym-node-status-api/src/logging.rs index 67913d28ae..c4581af169 100644 --- a/nym-node-status-api/nym-node-status-api/src/logging.rs +++ b/nym-node-status-api/nym-node-status-api/src/logging.rs @@ -39,7 +39,7 @@ pub(crate) fn setup_tracing_logger() -> anyhow::Result<()> { "hickory_resolver", ]; for crate_name in warn_crates { - filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))?); + filter = filter.add_directive(directive_checked(format!("{crate_name}=warn"))?); } let log_level_hint = filter.max_level_hint(); diff --git a/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs index 7461837fe0..0711299088 100644 --- a/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs +++ b/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs @@ -299,7 +299,7 @@ impl Monitor { let mut log_lines: Vec = vec![]; for (key, value) in nodes_summary.iter() { - log_lines.push(format!("{} = {}", key, value)); + log_lines.push(format!("{key} = {value}")); } tracing::info!("Directory summary: \n{}", log_lines.join("\n")); diff --git a/nym-node-status-api/nym-node-status-api/src/testruns/queue.rs b/nym-node-status-api/nym-node-status-api/src/testruns/queue.rs index fa44a9ef94..e36dc548f8 100644 --- a/nym-node-status-api/nym-node-status-api/src/testruns/queue.rs +++ b/nym-node-status-api/nym-node-status-api/src/testruns/queue.rs @@ -85,10 +85,7 @@ pub(crate) async fn try_queue_testrun( // save test run // let status = TestRunStatus::Queued as u32; - let log = format!( - "Test for {identity_key} requested at {} UTC\n\n", - timestamp_pretty - ); + let log = format!("Test for {identity_key} requested at {timestamp_pretty} UTC\n\n"); let id = sqlx::query!( "INSERT INTO testruns (gateway_id, status, ip_address, created_utc, log) VALUES (?, ?, ?, ?, ?)", diff --git a/nym-node-status-api/nym-node-status-client/src/lib.rs b/nym-node-status-api/nym-node-status-client/src/lib.rs index 39e802cde9..00e260e6b5 100644 --- a/nym-node-status-api/nym-node-status-client/src/lib.rs +++ b/nym-node-status-api/nym-node-status-client/src/lib.rs @@ -16,7 +16,7 @@ pub struct NsApiClient { impl NsApiClient { pub fn new(server_ip: &str, server_port: u16, auth_key: PrivateKey) -> Self { - let server_address = format!("{}:{}", server_ip, server_port); + let server_address = format!("{server_ip}:{server_port}"); let api = ApiPaths::new(server_address); let client = reqwest::Client::new(); diff --git a/nym-node/src/logging.rs b/nym-node/src/logging.rs index 332e274944..4852b68635 100644 --- a/nym-node/src/logging.rs +++ b/nym-node/src/logging.rs @@ -16,7 +16,7 @@ pub(crate) fn granual_filtered_env() -> anyhow::Result Result { let apis_client = NymApisClient::new(nym_apis, ShutdownToken::ephemeral())?; - if let Ok(rotation_info) = apis_client.get_key_rotation_info().await { + if let Ok(rotation_info) = apis_client.get_key_rotation_info().await.map(|r| r.details) { if rotation_info.is_epoch_stuck() { return Err(NymNodeError::StuckEpoch); } diff --git a/nym-node/src/node/key_rotation/controller.rs b/nym-node/src/node/key_rotation/controller.rs index 15fb11d3be..0aa8aa5fa5 100644 --- a/nym-node/src/node/key_rotation/controller.rs +++ b/nym-node/src/node/key_rotation/controller.rs @@ -7,7 +7,7 @@ use crate::node::nym_apis_client::NymApisClient; use crate::node::replay_protection::manager::ReplayProtectionBloomfiltersManager; use futures::pin_mut; use nym_task::ShutdownToken; -use nym_validator_client::models::{KeyRotationInfoResponse, KeyRotationState}; +use nym_validator_client::models::{KeyRotationDetails, KeyRotationInfoResponse, KeyRotationState}; use std::time::Duration; use time::OffsetDateTime; use tokio::time::{interval, sleep, Instant}; @@ -27,8 +27,8 @@ impl RotationConfig { impl From for RotationConfig { fn from(value: KeyRotationInfoResponse) -> Self { RotationConfig { - epoch_duration: value.epoch_duration, - rotation_state: value.key_rotation_state, + epoch_duration: value.details.epoch_duration, + rotation_state: value.details.key_rotation_state, } } } @@ -166,11 +166,11 @@ impl KeyRotationController { // unfortunate time, just wait until the next rotation rather than do all the work only to throw it // away immediately let Some(until_next_rotation) = key_rotation_info.until_next_rotation() else { - warn!("failed to determine time remaining until the next key rotation"); + debug!("key rotation is overdue - waiting..."); return NextAction::wait(Duration::from_secs(30)); }; if until_next_rotation < Duration::from_secs(30) { - debug!("less than 30s until next rotation - waiting until until then"); + debug!("less than 30s until next rotation - waiting until then"); return NextAction::wait(Duration::from_secs(30)); } @@ -265,13 +265,13 @@ impl KeyRotationController { NextAction::wait(Duration::from_secs(240)) } - async fn try_get_key_rotation_info(&self) -> Option { + async fn try_get_key_rotation_info(&self) -> Option { let Ok(rotation_info) = self.client.get_key_rotation_info().await else { warn!("failed to retrieve key rotation information from ANY nym-api - we might miss configuration changes"); return None; }; - Some(rotation_info) + Some(rotation_info.details) } async fn pre_announce_new_key(&self, rotation_id: u32) { diff --git a/nym-node/src/node/shared_network.rs b/nym-node/src/node/shared_network.rs index 2026ec1164..7fb88b31b0 100644 --- a/nym-node/src/node/shared_network.rs +++ b/nym-node/src/node/shared_network.rs @@ -11,6 +11,7 @@ use nym_node_metrics::prometheus_wrapper::{PrometheusMetric, PROMETHEUS_METRICS} use nym_noise::config::NoiseNetworkView; use nym_task::ShutdownToken; use nym_topology::node::RoutingNode; +use nym_topology::provider_trait::ToTopologyMetadata; use nym_topology::{ EntryDetails, EpochRewardedSet, NodeId, NymTopology, NymTopologyMetadata, Role, TopologyProvider, @@ -333,8 +334,7 @@ impl NetworkRefresher { self.noise_view.swap_view(noise_nodes); let mut network_guard = self.network.inner.write().await; - network_guard.topology_metadata = - NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id); + network_guard.topology_metadata = metadata.to_topology_metadata(); network_guard.network_nodes = nodes; network_guard.rewarded_set = rewarded_set; diff --git a/nym-node/src/throughput_tester/global_stats.rs b/nym-node/src/throughput_tester/global_stats.rs index 97239ddcee..e095c702c5 100644 --- a/nym-node/src/throughput_tester/global_stats.rs +++ b/nym-node/src/throughput_tester/global_stats.rs @@ -150,9 +150,7 @@ impl GlobalStatsUpdater { info!("wrote global stats to {}", global.display()); for (sender_id, records) in self.records.iter() { - let output = self - .output_directory - .join(format!("sender{}.csv", sender_id)); + let output = self.output_directory.join(format!("sender{sender_id}.csv")); let mut writer = csv::Writer::from_path(&output)?; for record in records { writer.serialize(record)?; diff --git a/nym-statistics-api/src/http/server.rs b/nym-statistics-api/src/http/server.rs index 3aa57c7da6..8f84f3e4fb 100644 --- a/nym-statistics-api/src/http/server.rs +++ b/nym-statistics-api/src/http/server.rs @@ -19,7 +19,7 @@ pub(crate) async fn build_http_api( let state = AppState::new(storage, cached_network).await; let router = router_builder.with_state(state); - let bind_addr = format!("0.0.0.0:{}", http_port); + let bind_addr = format!("0.0.0.0:{http_port}"); tracing::info!("Binding server to {bind_addr}"); router.build_server(bind_addr).await diff --git a/nym-statistics-api/src/logging.rs b/nym-statistics-api/src/logging.rs index 1adfa31b71..3c5a3834e0 100644 --- a/nym-statistics-api/src/logging.rs +++ b/nym-statistics-api/src/logging.rs @@ -32,7 +32,7 @@ pub(crate) fn setup_tracing_logger() -> anyhow::Result<()> { "hyper_util", ]; for crate_name in warn_crates { - filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))?); + filter = filter.add_directive(directive_checked(format!("{crate_name}=warn"))?); } let log_level_hint = filter.max_level_hint(); diff --git a/nym-validator-rewarder/src/rewarder/ticketbook_issuance/verifier.rs b/nym-validator-rewarder/src/rewarder/ticketbook_issuance/verifier.rs index 8cca3f062e..fd04e18de0 100644 --- a/nym-validator-rewarder/src/rewarder/ticketbook_issuance/verifier.rs +++ b/nym-validator-rewarder/src/rewarder/ticketbook_issuance/verifier.rs @@ -349,6 +349,8 @@ impl IssuerUnderTest { } }; + self.issued_commitment = Some(issued_ticketbooks.clone()); + // 1. check if the signature on the response matches if !issued_ticketbooks.verify_signature(&self.details.public_key) { error!("❗ RESPONSE SIGNATURE MISMATCH ❗"); @@ -374,7 +376,6 @@ impl IssuerUnderTest { .merkle_root_hex() .unwrap_or_default() ); - self.issued_commitment = Some(issued_ticketbooks) } async fn issue_deposit_challenge( @@ -429,6 +430,8 @@ impl IssuerUnderTest { } }; + self.challenge_commitment_response = Some(challenge_commitment.clone()); + // 2. check if the signature on the response matches if !challenge_commitment.verify_signature(&self.details.public_key) { error!("❗ RESPONSE SIGNATURE MISMATCH ❗"); @@ -501,7 +504,6 @@ impl IssuerUnderTest { } info!("✅ obtained issued ticketbooks challenge commitment"); - self.challenge_commitment_response = Some(challenge_commitment) } fn verify_partial_ticketbook( diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 24f918e136..aa6521d770 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -4229,6 +4229,7 @@ version = "0.1.0" dependencies = [ "bincode", "serde", + "serde_json", "tracing", ] diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 548f135173..b5c1ccdfba 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -145,8 +145,8 @@ impl Config { .map_err(io::Error::other) .map(|toml| fs::write(location.clone(), toml)) { - Ok(_) => log::debug!("Writing to: {:#?}", location), - Err(err) => log::warn!("Failed to write to {:#?}: {err}", location), + Ok(_) => log::debug!("Writing to: {location:#?}"), + Err(err) => log::warn!("Failed to write to {location:#?}: {err}"), } } @@ -165,8 +165,8 @@ impl Config { .map_err(io::Error::other) .map(|toml| fs::write(location.clone(), toml)) { - Ok(_) => log::debug!("Writing to: {:#?}", location), - Err(err) => log::warn!("Failed to write to {:#?}: {err}", location), + Ok(_) => log::debug!("Writing to: {location:#?}"), + Err(err) => log::warn!("Failed to write to {location:#?}: {err}"), } } Ok(()) @@ -178,11 +178,11 @@ impl Config { let file = Self::config_file_path(None); match load_from_file::(file.clone()) { Ok(global) => { - log::debug!("Loaded from file {:#?}", file); + log::debug!("Loaded from file {file:#?}"); Some(global) } Err(err) => { - log::trace!("Not loading {:#?}: {err}", file); + log::trace!("Not loading {file:#?}: {err}"); None } } @@ -194,10 +194,10 @@ impl Config { let file = Self::config_file_path(Some(network)); match load_from_file::(file.clone()) { Ok(config) => { - log::trace!("Loaded from file {:#?}", file); + log::trace!("Loaded from file {file:#?}"); networks.insert(network.as_key(), config); } - Err(err) => log::trace!("Not loading {:#?}: {err}", file), + Err(err) => log::trace!("Not loading {file:#?}: {err}"), }; } diff --git a/nym-wallet/src-tauri/src/network_config.rs b/nym-wallet/src-tauri/src/network_config.rs index 6f75b6e5fa..c0f14a65bd 100644 --- a/nym-wallet/src-tauri/src/network_config.rs +++ b/nym-wallet/src-tauri/src/network_config.rs @@ -33,7 +33,7 @@ pub async fn get_selected_nyxd_url( ) -> Result, BackendError> { let state = state.read().await; let url = state.get_selected_nyxd_url(&network).map(String::from); - log::info!("Selected nyxd url for {network}: {:?}", url); + log::info!("Selected nyxd url for {network}: {url:?}"); Ok(url) } @@ -44,7 +44,7 @@ pub async fn get_default_nyxd_url( ) -> Result { let state = state.read().await; let url = state.get_default_nyxd_url(&network).map(String::from); - log::info!("Default nyxd url for {network}: {:?}", url); + log::info!("Default nyxd url for {network}: {url:?}"); url.ok_or_else(|| BackendError::WalletNoDefaultValidator) } diff --git a/nym-wallet/src-tauri/src/operations/app/link.rs b/nym-wallet/src-tauri/src/operations/app/link.rs index fa41aa8f19..d238242633 100644 --- a/nym-wallet/src-tauri/src/operations/app/link.rs +++ b/nym-wallet/src-tauri/src/operations/app/link.rs @@ -2,10 +2,10 @@ use tauri_plugin_opener::OpenerExt; #[tauri::command] pub async fn open_url(url: String, app_handle: tauri::AppHandle) -> Result<(), String> { - println!("Opening URL: {}", url); + println!("Opening URL: {url}"); match app_handle.opener().open_url(&url, None::<&str>) { Ok(_) => Ok(()), - Err(err) => Err(format!("Failed to open URL: {}", err)), + Err(err) => Err(format!("Failed to open URL: {err}")), } } diff --git a/nym-wallet/src-tauri/src/operations/app/version.rs b/nym-wallet/src-tauri/src/operations/app/version.rs index d904449cf0..d69a80f5e3 100644 --- a/nym-wallet/src-tauri/src/operations/app/version.rs +++ b/nym-wallet/src-tauri/src/operations/app/version.rs @@ -10,13 +10,13 @@ pub async fn check_version(handle: tauri::AppHandle) -> Result>> Getting app version info"); let updater = handle.updater().map_err(|e| { - log::error!("Failed to get updater: {}", e); + log::error!("Failed to get updater: {e}"); BackendError::CheckAppVersionError })?; // Then check for updates let update_info = updater.check().await.map_err(|e| { - log::error!("An error occurred while checking for app update {}", e); + log::error!("An error occurred while checking for app update {e}"); BackendError::CheckAppVersionError })?; @@ -35,10 +35,7 @@ pub async fn check_version(handle: tauri::AppHandle) -> Result Result<(), BackendError> { // create the new window first, to stop the app process from exiting - log::info!("Creating {} window...", new_window_label); + log::info!("Creating {new_window_label} window..."); match tauri::WebviewWindowBuilder::new( &app_handle, new_window_label, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 3750e21aee..3319363be1 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -108,7 +108,7 @@ async fn _connect_with_mnemonic( "{}", state.get_config_validator_entries(network).format(",\n") ); - log::debug!("List of validators for {network}: [\n{}\n]", f,); + log::debug!("List of validators for {network}: [\n{f}\n]",); } state.config().clone() @@ -598,7 +598,7 @@ pub async fn list_accounts( address: account.addresses[&network].to_string(), }) .map(|account| { - log::trace!("{:?}", account); + log::trace!("{account:?}"); account }) .collect(); diff --git a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs index ee9e0e3742..881b54aa3c 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs @@ -71,7 +71,7 @@ pub async fn bond_gateway( .bond_gateway(gateway, msg_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -84,10 +84,10 @@ pub async fn unbond_gateway( ) -> Result { let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); - log::info!(">>> Unbond gateway, fee = {:?}", fee); + log::info!(">>> Unbond gateway, fee = {fee:?}"); let res = guard.current_client()?.nyxd.unbond_gateway(fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -136,7 +136,7 @@ pub async fn bond_mixnode( .bond_mixnode(mixnode, cost_params, msg_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -184,7 +184,7 @@ pub async fn bond_nymnode( .bond_nymnode(nymnode, cost_params, msg_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -232,7 +232,7 @@ pub async fn update_pledge( }; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, @@ -249,10 +249,7 @@ pub async fn pledge_more( let additional_pledge_base = guard.attempt_convert_to_base_coin(additional_pledge.clone())?; let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Pledge more, additional_pledge_display = {}, additional_pledge_base = {}, fee = {:?}", - additional_pledge, - additional_pledge_base, - fee, + ">>> Pledge more, additional_pledge_display = {additional_pledge}, additional_pledge_base = {additional_pledge_base}, fee = {fee:?}", ); let res = guard .current_client()? @@ -260,7 +257,7 @@ pub async fn pledge_more( .pledge_more(additional_pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -276,10 +273,7 @@ pub async fn decrease_pledge( let decrease_by_base = guard.attempt_convert_to_base_coin(decrease_by.clone())?; let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Decrease pledge, pledge_decrease_display = {}, pledge_decrease_base = {}, fee = {:?}", - decrease_by, - decrease_by_base, - fee, + ">>> Decrease pledge, pledge_decrease_display = {decrease_by}, pledge_decrease_base = {decrease_by_base}, fee = {fee:?}", ); let res = guard .current_client()? @@ -287,7 +281,7 @@ pub async fn decrease_pledge( .decrease_pledge(decrease_by_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -300,10 +294,10 @@ pub async fn unbond_mixnode( ) -> Result { let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); - log::info!(">>> Unbond mixnode, fee = {:?}", fee); + log::info!(">>> Unbond mixnode, fee = {fee:?}"); let res = guard.current_client()?.nyxd.unbond_mixnode(fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -319,7 +313,7 @@ pub async fn unbond_nymnode( log::info!(">>> Unbond NymNode, fee = {fee:?}"); let res = guard.current_client()?.nyxd.unbond_nymnode(fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, @@ -347,7 +341,7 @@ pub async fn update_mixnode_cost_params( .update_cost_params(cost_params, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -372,7 +366,7 @@ pub async fn update_mixnode_config( .update_mixnode_config(update, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -397,7 +391,7 @@ pub async fn update_gateway_config( .update_gateway_config(update, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -420,14 +414,14 @@ pub async fn get_mixnode_avg_uptime( match res.mixnode_details { Some(details) => { let id = details.mix_id(); - log::trace!(" >>> Get average uptime percentage: mix_id = {}", id); + log::trace!(" >>> Get average uptime percentage: mix_id = {id}"); let avg_uptime_percent = client .nym_api .get_mixnode_avg_uptime(id) .await .ok() .map(|r| r.avg_uptime); - log::trace!(" <<< {:?}", avg_uptime_percent); + log::trace!(" <<< {avg_uptime_percent:?}"); Ok(avg_uptime_percent) } None => Ok(None), @@ -461,7 +455,7 @@ pub async fn mixnode_bond_details( &r.bond_information.mix_node.identity_key )) ); - log::trace!("<<< {:?}", details); + log::trace!("<<< {details:?}"); Ok(details) } @@ -490,7 +484,7 @@ pub async fn gateway_bond_details( "<<< identity_key = {:?}", res.as_ref().map(|r| r.gateway.identity_key.to_string()) ); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(res) } @@ -521,7 +515,7 @@ pub async fn nym_node_bond_details( &r.bond_information.node.identity_key )) ); - log::trace!("<<< {:?}", details); + log::trace!("<<< {details:?}"); Ok(details) } @@ -530,7 +524,7 @@ pub async fn get_pending_operator_rewards( address: String, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Get pending operator rewards for {}", address); + log::info!(">>> Get pending operator rewards for {address}"); let guard = state.read().await; let res = guard .current_client()? @@ -554,11 +548,7 @@ pub async fn get_pending_operator_rewards( .transpose()? .unwrap_or_else(|| guard.default_zero_mix_display_coin()); - log::info!( - "<<< rewards_base = {:?}, rewards_display = {}", - base_coin, - display_coin - ); + log::info!("<<< rewards_base = {base_coin:?}, rewards_display = {display_coin}"); Ok(display_coin) } @@ -637,7 +627,7 @@ pub async fn migrate_legacy_mixnode( let res = client.nyxd.migrate_legacy_mixnode(fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -657,7 +647,7 @@ pub async fn migrate_legacy_gateway( let res = client.nyxd.migrate_legacy_gateway(None, fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -678,7 +668,7 @@ pub async fn update_nymnode_config( .update_nymnode_config(update, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -705,7 +695,7 @@ pub async fn update_nymnode_cost_params( .update_cost_params(cost_params, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) diff --git a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs index 8fc47187d0..5275436dcd 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs @@ -62,10 +62,7 @@ pub async fn get_pending_delegation_events( "<<< {} pending delegation events", client_specific_events.len() ); - log::trace!( - "<<< pending delegation events = {:?}", - client_specific_events - ); + log::trace!("<<< pending delegation events = {client_specific_events:?}"); Ok(client_specific_events) } @@ -83,15 +80,11 @@ pub async fn delegate_to_mixnode( let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Delegate to mixnode: mix_id = {}, display_amount = {}, base_amount = {}, fee = {:?}", - mix_id, - amount, - delegation_base, - fee, + ">>> Delegate to mixnode: mix_id = {mix_id}, display_amount = {amount}, base_amount = {delegation_base}, fee = {fee:?}", ); let res = client.nyxd.delegate(mix_id, delegation_base, fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -106,14 +99,10 @@ pub async fn undelegate_from_mixnode( let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); - log::info!( - ">>> Undelegate from mixnode: mix_id = {}, fee = {:?}", - mix_id, - fee - ); + log::info!(">>> Undelegate from mixnode: mix_id = {mix_id}, fee = {fee:?}"); let res = guard.current_client()?.nyxd.undelegate(mix_id, fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -128,11 +117,7 @@ pub async fn undelegate_all_from_mixnode( state: tauri::State<'_, WalletState>, ) -> Result, BackendError> { log::info!( - ">>> Undelegate all from mixnode: mix_id = {}, uses_vesting_contract_tokens = {}, fee_liquid = {:?}, fee_vesting = {:?}", - mix_id, - uses_vesting_contract_tokens, - fee_liquid, - fee_vesting, + ">>> Undelegate all from mixnode: mix_id = {mix_id}, uses_vesting_contract_tokens = {uses_vesting_contract_tokens}, fee_liquid = {fee_liquid:?}, fee_vesting = {fee_vesting:?}", ); let mut res: Vec = vec![undelegate_from_mixnode(mix_id, fee_liquid, state.clone()).await?]; @@ -178,7 +163,7 @@ pub(crate) async fn get_node_information( let str_err = format!( "Failed to get legacy mixnode details for node_id = {node_id}. Error: {err}", ); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); })? .mixnode_details; @@ -222,7 +207,7 @@ pub async fn get_all_mix_delegations( .get_all_delegator_delegations(&address) .await .tap_err(|err| { - log::error!(" <<< Failed to get delegations. Error: {}", err); + log::error!(" <<< Failed to get delegations. Error: {err}"); })?; log::info!(" <<< {} delegations", delegations.len()); @@ -230,7 +215,7 @@ pub async fn get_all_mix_delegations( get_pending_delegation_events(state.clone()) .await .tap_err(|err| { - log::error!(" <<< Failed to get pending delegations. Error: {}", err); + log::error!(" <<< Failed to get pending delegations. Error: {err}"); })?; log::info!( @@ -276,7 +261,7 @@ pub async fn get_all_mix_delegations( "Failed to get operator rewards as a display coin for mix_id = {}. Error: {}", d.mix_id, err ); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); }) .unwrap_or_default(); @@ -295,7 +280,7 @@ pub async fn get_all_mix_delegations( "Failed to get delegator rewards as a display coin for mix_id = {}. Error: {}", d.mix_id, err ); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); }) .unwrap_or_default(); @@ -314,12 +299,12 @@ pub async fn get_all_mix_delegations( "Failed to mixnode cost params for mix_id = {}. Error: {}", d.mix_id, err ); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); }) .unwrap_or_default(); - log::trace!(" >>> Get accumulated rewards: address = {}", address); + log::trace!(" >>> Get accumulated rewards: address = {address}"); let pending_reward = client .nyxd .get_pending_delegator_reward(&address, d.mix_id, d.proxy.clone()) @@ -329,7 +314,7 @@ pub async fn get_all_mix_delegations( "Failed to get accumulated rewards for mix_id = {}. Error: {}", d.mix_id, err ); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); }) .unwrap_or_default(); @@ -340,15 +325,11 @@ pub async fn get_all_mix_delegations( .attempt_convert_to_display_dec_coin(reward.clone().into()) .tap_err(|err| { let str_err = format!("Failed to get convert reward to a display coin for mix_id = {}. Error: {}", d.mix_id, err); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); }) .ok(); - log::trace!( - " <<< rewards = {:?}, amount = {:?}", - pending_reward, - amount - ); + log::trace!(" <<< rewards = {pending_reward:?}, amount = {amount:?}"); amount } None => { @@ -367,7 +348,7 @@ pub async fn get_all_mix_delegations( "Failed to get stake saturation for mix_id = {}. Error: {}", d.mix_id, err ); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); }) .unwrap_or(MixStakeSaturationResponse { @@ -375,7 +356,7 @@ pub async fn get_all_mix_delegations( uncapped_saturation: None, current_saturation: None, }); - log::trace!(" <<< {:?}", stake_saturation); + log::trace!(" <<< {stake_saturation:?}"); log::trace!( " >>> Get average uptime percentage: mix_iid = {}", @@ -391,7 +372,7 @@ pub async fn get_all_mix_delegations( "Failed to get current node performance for node_id = {}. Error: {err}", d.mix_id ); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); }) .ok() @@ -400,7 +381,7 @@ pub async fn get_all_mix_delegations( // convert to old u8 let current_uptime = current_performance.map(|p| (p * 100.) as u8); - log::trace!(" <<< {:?}", current_uptime); + log::trace!(" <<< {current_uptime:?}"); log::trace!( " >>> Convert delegated on block height to timestamp: block_height = {}", @@ -415,19 +396,17 @@ pub async fn get_all_mix_delegations( // Check if the error is related to height not being available (pruning) if error_message.contains("height") && error_message.contains("not available") { let str_err = "Due to pruning strategies from validators, please navigate to the Settings tab and change your RPC node for your validator to retrieve your delegations."; - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err.to_string()); } else { let str_err = format!("Failed to get block timestamp for height = {} for delegation to mix_id = {}. Error: {}", d.height, d.mix_id, err); - log::error!(" <<< {}", str_err); + log::error!(" <<< {str_err}"); error_strings.push(str_err); } }).ok(); let delegated_on_iso_datetime = timestamp.map(|ts| ts.to_rfc3339()); log::trace!( - " <<< timestamp = {:?}, delegated_on_iso_datetime = {:?}", - timestamp, - delegated_on_iso_datetime + " <<< timestamp = {timestamp:?}, delegated_on_iso_datetime = {delegated_on_iso_datetime:?}" ); let pending_events = filter_pending_events(d.mix_id, &pending_events_for_account); @@ -466,7 +445,7 @@ pub async fn get_all_mix_delegations( }, }) } - log::trace!("<<< {:?}", with_everything); + log::trace!("<<< {with_everything:?}"); Ok(with_everything) } @@ -490,11 +469,7 @@ pub async fn get_pending_delegator_rewards( proxy: Option, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!( - ">>> Get pending delegator rewards: mix_id = {}, proxy = {:?}", - mix_id, - proxy - ); + log::info!(">>> Get pending delegator rewards: mix_id = {mix_id}, proxy = {proxy:?}"); let guard = state.read().await; let res = guard .current_client()? @@ -518,11 +493,7 @@ pub async fn get_pending_delegator_rewards( .transpose()? .unwrap_or_else(|| guard.default_zero_mix_display_coin()); - log::info!( - "<<< rewards_base = {:?}, rewards_display = {}", - base_coin, - display_coin - ); + log::info!("<<< rewards_base = {base_coin:?}, rewards_display = {display_coin}"); Ok(display_coin) } @@ -554,7 +525,7 @@ pub async fn get_delegation_summary( total_delegations, total_rewards ); - log::trace!("<<< {:?}", delegations); + log::trace!("<<< {delegations:?}"); Ok(DelegationsSummaryResponse { delegations, diff --git a/nym-wallet/src-tauri/src/operations/mixnet/interval.rs b/nym-wallet/src-tauri/src/operations/mixnet/interval.rs index 05151d8a53..8268f44242 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/interval.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/interval.rs @@ -14,7 +14,7 @@ pub async fn get_current_interval( ) -> Result { log::info!(">>> Get current interval"); let res = nyxd_client!(state).get_current_interval_details().await?; - log::info!("<<< current interval = {:?}", res); + log::info!("<<< current interval = {res:?}"); Ok(res.interval.into()) } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs b/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs index 4563005b88..b32a4224b3 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs @@ -23,7 +23,7 @@ pub async fn claim_operator_reward( .withdraw_operator_reward(fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -44,7 +44,7 @@ pub async fn claim_delegator_reward( .withdraw_delegator_reward(node_id, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -87,9 +87,7 @@ pub async fn claim_locked_and_unlocked_delegator_reward( let did_delegate_with_vesting_contract = vesting_delegation.delegation.is_some(); log::trace!( - "<<< Delegations done with: mixnet contract = {}, vesting contract = {}", - did_delegate_with_mixnet_contract, - did_delegate_with_vesting_contract + "<<< Delegations done with: mixnet contract = {did_delegate_with_mixnet_contract}, vesting contract = {did_delegate_with_vesting_contract}" ); let mut res: Vec = vec![]; @@ -99,7 +97,7 @@ pub async fn claim_locked_and_unlocked_delegator_reward( if did_delegate_with_vesting_contract { res.push(vesting_claim_delegator_reward(node_id, fee, state).await?); } - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(res) } diff --git a/nym-wallet/src-tauri/src/operations/mixnet/send.rs b/nym-wallet/src-tauri/src/operations/mixnet/send.rs index 9e1ae7849c..5a313608ca 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/send.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/send.rs @@ -20,12 +20,7 @@ pub async fn send( let from_address = guard.current_client()?.nyxd.address().to_string(); let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Send: display_amount = {}, base_amount = {}, from = {}, to = {}, fee = {:?}", - amount, - amount_base, - from_address, - to_address, - fee, + ">>> Send: display_amount = {amount}, base_amount = {amount_base}, from = {from_address}, to = {to_address}, fee = {fee:?}", ); let raw_res = guard .current_client()? @@ -38,6 +33,6 @@ pub async fn send( TransactionDetails::new(amount, from_address, to_address.to_string()), fee_amount, ); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(res) } diff --git a/nym-wallet/src-tauri/src/operations/signatures/sign.rs b/nym-wallet/src-tauri/src/operations/signatures/sign.rs index 59da5cae36..e93edd57f4 100644 --- a/nym-wallet/src-tauri/src/operations/signatures/sign.rs +++ b/nym-wallet/src-tauri/src/operations/signatures/sign.rs @@ -40,7 +40,7 @@ pub async fn sign( signature_as_hex: signature.to_string(), }; let output_json = json!(output).to_string(); - log::info!(">>> Signing data {}", output_json); + log::info!(">>> Signing data {output_json}"); Ok(output_json) } @@ -48,17 +48,17 @@ async fn get_pubkey_from_account_address( address: &AccountId, state: &tauri::State<'_, WalletState>, ) -> Result { - log::info!("Getting public key for address {} from chain...", address); + log::info!("Getting public key for address {address} from chain..."); let guard = state.read().await; let client = guard.current_client()?; let account = client.nyxd.get_account(address).await?.ok_or_else(|| { - log::error!("No account associated with address {}", address); + log::error!("No account associated with address {address}"); BackendError::SignatureError(format!("No account associated with address {address}")) })?; let base_account = account.try_get_base_account()?; base_account.pubkey.ok_or_else(|| { - log::error!("No pubkey found for address {}", address); + log::error!("No pubkey found for address {address}"); BackendError::SignatureError(format!("No pubkey found for address {address}")) }) } @@ -125,7 +125,7 @@ pub async fn verify( )); } - log::info!("<<< Verifying signature [{}]", signature_as_hex); + log::info!("<<< Verifying signature [{signature_as_hex}]"); let verifying_key = VerifyingKey::from_sec1_bytes(&public_key.to_bytes())?; let signature = Signature::from_str(&signature_as_hex)?; let message_as_bytes = message.into_bytes(); diff --git a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs index eb1b4bb1cf..3866062e2f 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs @@ -114,17 +114,11 @@ pub async fn simulate_update_pledge( match new_pledge.amount.cmp(¤t_pledge.amount) { Ordering::Greater => { - log::info!( - "Simulate pledge increase, calculated additional pledge {}", - dec_delta, - ); + log::info!("Simulate pledge increase, calculated additional pledge {dec_delta}",); simulate_mixnet_operation(ExecuteMsg::PledgeMore {}, Some(dec_delta), &state).await } Ordering::Less => { - log::info!( - "Simulate pledge reduction, calculated reduction pledge {}", - dec_delta, - ); + log::info!("Simulate pledge reduction, calculated reduction pledge {dec_delta}",); simulate_mixnet_operation( ExecuteMsg::DecreasePledge { decrease_by: guard.attempt_convert_to_base_coin(dec_delta)?.into(), diff --git a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs index 9578575c3f..28752f4bad 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs @@ -114,8 +114,7 @@ pub async fn simulate_vesting_update_pledge( })? .into(); log::info!( - ">>> Simulate pledge more, calculated additional pledge {}", - additional_pledge, + ">>> Simulate pledge more, calculated additional pledge {additional_pledge}", ); simulate_vesting_operation( ExecuteMsg::PledgeMore { @@ -134,8 +133,7 @@ pub async fn simulate_vesting_update_pledge( })? .into(); log::info!( - ">>> Simulate decrease pledge, calculated decrease pledge {}", - decrease_pledge, + ">>> Simulate decrease pledge, calculated decrease pledge {decrease_pledge}", ); simulate_vesting_operation( ExecuteMsg::DecreasePledge { diff --git a/nym-wallet/src-tauri/src/operations/vesting/bond.rs b/nym-wallet/src-tauri/src/operations/vesting/bond.rs index dfa7aec1f9..9a97b2bfe6 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/bond.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/bond.rs @@ -48,7 +48,7 @@ pub async fn vesting_bond_gateway( .vesting_bond_gateway(gateway, msg_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -61,13 +61,10 @@ pub async fn vesting_unbond_gateway( ) -> Result { let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); - log::info!( - ">>> Unbond gateway bonded with locked tokens, fee = {:?}", - fee - ); + log::info!(">>> Unbond gateway bonded with locked tokens, fee = {fee:?}"); let res = nyxd_client!(state).vesting_unbond_gateway(fee).await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -118,7 +115,7 @@ pub async fn vesting_bond_mixnode( .vesting_bond_mixnode(mixnode, cost_params, msg_signature, pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -144,9 +141,7 @@ pub async fn vesting_update_pledge( let res = match new_pledge.amount.cmp(¤t_pledge.amount) { Ordering::Greater => { log::info!( - "Pledge increase with locked tokens, calculated additional pledge {}, fee = {:?}", - dec_delta, - fee, + "Pledge increase with locked tokens, calculated additional pledge {dec_delta}, fee = {fee:?}", ); guard .current_client()? @@ -156,9 +151,7 @@ pub async fn vesting_update_pledge( } Ordering::Less => { log::info!( - "Pledge reduction with locked tokens, calculated reduction pledge {}, fee = {:?}", - dec_delta, - fee, + "Pledge reduction with locked tokens, calculated reduction pledge {dec_delta}, fee = {fee:?}", ); guard .current_client()? @@ -170,7 +163,7 @@ pub async fn vesting_update_pledge( }; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, @@ -187,10 +180,7 @@ pub async fn vesting_pledge_more( let additional_pledge_base = guard.attempt_convert_to_base_coin(additional_pledge.clone())?; let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Pledge more with locked tokens, additional_pledge_display = {}, additional_pledge_base = {}, fee = {:?}", - additional_pledge, - additional_pledge_base, - fee, + ">>> Pledge more with locked tokens, additional_pledge_display = {additional_pledge}, additional_pledge_base = {additional_pledge_base}, fee = {fee:?}", ); let res = guard .current_client()? @@ -198,7 +188,7 @@ pub async fn vesting_pledge_more( .vesting_pledge_more(additional_pledge_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -214,10 +204,7 @@ pub async fn vesting_decrease_pledge( let decrease_by_base = guard.attempt_convert_to_base_coin(decrease_by.clone())?; let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Decrease pledge with locked tokens, pledge_decrease_display = {}, pledge_decrease_base = {}, fee = {:?}", - decrease_by, - decrease_by_base, - fee, + ">>> Decrease pledge with locked tokens, pledge_decrease_display = {decrease_by}, pledge_decrease_base = {decrease_by_base}, fee = {fee:?}", ); let res = guard .current_client()? @@ -225,7 +212,7 @@ pub async fn vesting_decrease_pledge( .vesting_decrease_pledge(decrease_by_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -238,17 +225,14 @@ pub async fn vesting_unbond_mixnode( ) -> Result { let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); - log::info!( - ">>> Unbond mixnode bonded with locked tokens, fee = {:?}", - fee - ); + log::info!(">>> Unbond mixnode bonded with locked tokens, fee = {fee:?}"); let res = guard .current_client()? .nyxd .vesting_unbond_mixnode(fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -265,10 +249,7 @@ pub async fn withdraw_vested_coins( let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Withdraw vested liquid coins: amount_base = {}, amount_base = {}, fee = {:?}", - amount, - amount_base, - fee + ">>> Withdraw vested liquid coins: amount_base = {amount}, amount_base = {amount_base}, fee = {fee:?}" ); let res = guard .current_client()? @@ -276,7 +257,7 @@ pub async fn withdraw_vested_coins( .withdraw_vested_coins(amount_base, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -304,7 +285,7 @@ pub async fn vesting_update_mixnode_cost_params( .vesting_update_mixnode_cost_params(cost_params, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -329,7 +310,7 @@ pub async fn vesting_update_mixnode_config( .vesting_update_mixnode_config(update, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -354,7 +335,7 @@ pub async fn vesting_update_gateway_config( .vesting_update_gateway_config(update, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) diff --git a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs index bbb1d8df11..ed896c0322 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs @@ -20,11 +20,7 @@ pub async fn vesting_delegate_to_mixnode( let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Delegate to mixnode with locked tokens: mix_id = {}, amount_display = {}, amount_base = {}, fee = {:?}", - mix_id, - amount, - delegation, - fee + ">>> Delegate to mixnode with locked tokens: mix_id = {mix_id}, amount_display = {amount}, amount_base = {delegation}, fee = {fee:?}" ); let res = guard .current_client()? @@ -32,7 +28,7 @@ pub async fn vesting_delegate_to_mixnode( .vesting_delegate_to_mixnode(mix_id, delegation, None, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -47,9 +43,7 @@ pub async fn vesting_undelegate_from_mixnode( let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); log::info!( - ">>> Undelegate from mixnode delegated with locked tokens: mix_id = {}, fee = {:?}", - mix_id, - fee, + ">>> Undelegate from mixnode delegated with locked tokens: mix_id = {mix_id}, fee = {fee:?}", ); let res = guard .current_client()? @@ -57,7 +51,7 @@ pub async fn vesting_undelegate_from_mixnode( .vesting_undelegate_from_mixnode(mix_id, None, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) diff --git a/nym-wallet/src-tauri/src/operations/vesting/migrate.rs b/nym-wallet/src-tauri/src/operations/vesting/migrate.rs index 00257464b1..6d99641f87 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/migrate.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/migrate.rs @@ -25,7 +25,7 @@ pub async fn migrate_vested_mixnode( .migrate_vested_mixnode(fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -52,7 +52,7 @@ pub async fn migrate_vested_delegations( .get_all_delegator_delegations(&address) .await .inspect_err(|err| { - log::error!(" <<< Failed to get delegations. Error: {}", err); + log::error!(" <<< Failed to get delegations. Error: {err}"); })?; log::info!(" <<< {} delegations", delegations.len()); @@ -91,6 +91,6 @@ pub async fn migrate_vested_delegations( .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result(res, None)?) } diff --git a/nym-wallet/src-tauri/src/operations/vesting/queries.rs b/nym-wallet/src-tauri/src/operations/vesting/queries.rs index f3377d5ed5..750d03caee 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/queries.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/queries.rs @@ -146,7 +146,7 @@ pub(crate) async fn vesting_start_time( .vesting_start_time(vesting_account_address) .await? .seconds(); - log::info!("<<< vesting start time = {}", res); + log::info!("<<< vesting start time = {res}"); Ok(res) } @@ -160,7 +160,7 @@ pub(crate) async fn vesting_end_time( .vesting_end_time(vesting_account_address) .await? .seconds(); - log::info!("<<< vesting end time = {}", res); + log::info!("<<< vesting end time = {res}"); Ok(res) } @@ -180,7 +180,7 @@ pub(crate) async fn original_vesting( .await?; let res = OriginalVestingResponse::from_vesting_contract(res, reg)?; - log::info!("<<< {:?}", res); + log::info!("<<< {res:?}"); Ok(res) } @@ -347,7 +347,7 @@ pub(crate) async fn vesting_get_mixnode_pledge( .map(|pledge| PledgeData::from_vesting_contract(pledge, reg)) .transpose()?; - log::info!("<<< {:?}", res); + log::info!("<<< {res:?}"); Ok(res) } @@ -368,7 +368,7 @@ pub(crate) async fn vesting_get_gateway_pledge( .map(|pledge| PledgeData::from_vesting_contract(pledge, reg)) .transpose()?; - log::info!("<<< {:?}", res); + log::info!("<<< {res:?}"); Ok(res) } @@ -381,7 +381,7 @@ pub(crate) async fn get_current_vesting_period( let res = nyxd_client!(state) .get_current_vesting_period(address) .await?; - log::info!("<<< {:?}", res); + log::info!("<<< {res:?}"); Ok(res) } @@ -397,6 +397,6 @@ pub(crate) async fn get_account_info( let vesting_account = guard.current_client()?.nyxd.get_account(address).await?; let res = VestingAccountInfo::from_vesting_contract(vesting_account, res)?; - log::info!("<<< {:?}", res); + log::info!("<<< {res:?}"); Ok(res) } diff --git a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs index 7cf2155cdb..f41fee9055 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs @@ -22,7 +22,7 @@ pub async fn vesting_claim_operator_reward( .vesting_withdraw_operator_reward(None) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) @@ -34,10 +34,7 @@ pub async fn vesting_claim_delegator_reward( fee: Option, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!( - ">>> Vesting account: claim delegator reward: mix_id = {}", - mix_id - ); + log::info!(">>> Vesting account: claim delegator reward: mix_id = {mix_id}"); let guard = state.read().await; let fee_amount = guard.convert_tx_fee(fee.as_ref()); let res = guard @@ -46,7 +43,7 @@ pub async fn vesting_claim_delegator_reward( .vesting_withdraw_delegator_reward(mix_id, fee) .await?; log::info!("<<< tx hash = {}", res.transaction_hash); - log::trace!("<<< {:?}", res); + log::trace!("<<< {res:?}"); Ok(TransactionExecuteResult::from_execute_result( res, fee_amount, )?) diff --git a/nym-wallet/src-tauri/src/wallet_storage/mod.rs b/nym-wallet/src-tauri/src/wallet_storage/mod.rs index 973172f4fc..42f9f080fe 100644 --- a/nym-wallet/src-tauri/src/wallet_storage/mod.rs +++ b/nym-wallet/src-tauri/src/wallet_storage/mod.rs @@ -286,7 +286,7 @@ fn remove_login_at_file(filepath: &Path, id: &LoginId) -> Result<(), BackendErro let mut stored_wallet = load_existing_wallet_at_file(filepath)?; if stored_wallet.is_empty() { - log::info!("Removing file: {:#?}", filepath); + log::info!("Removing file: {filepath:#?}"); return Ok(fs::remove_file(filepath)?); } @@ -295,7 +295,7 @@ fn remove_login_at_file(filepath: &Path, id: &LoginId) -> Result<(), BackendErro .ok_or(BackendError::WalletNoSuchLoginId)?; if stored_wallet.is_empty() { - log::info!("Removing file: {:#?}", filepath); + log::info!("Removing file: {filepath:#?}"); Ok(fs::remove_file(filepath)?) } else { write_to_file(filepath, &stored_wallet) @@ -345,7 +345,7 @@ fn _archive_wallet_file(path: &Path) -> Result<(), BackendError> { additional_number += 1; } else { if let Some(new_path) = new_path.to_str() { - log::info!("Archived to: {}", new_path); + log::info!("Archived to: {new_path}"); } else { log::warn!("Archived wallet file to filename that is not a valid UTF-8 string"); } @@ -363,17 +363,14 @@ pub(crate) fn archive_wallet_file() -> Result<(), BackendError> { if filepath.exists() { if let Some(filepath) = filepath.to_str() { - log::info!("Archiving wallet file: {}", filepath); + log::info!("Archiving wallet file: {filepath}"); } else { log::info!("Archiving wallet file"); } _archive_wallet_file(&filepath) } else { if let Some(filepath) = filepath.to_str() { - log::info!( - "Skipping archiving wallet file, as it's not found: {}", - filepath - ); + log::info!("Skipping archiving wallet file, as it's not found: {filepath}"); } else { log::info!("Skipping archiving wallet file, as it's not found"); } @@ -430,7 +427,7 @@ fn remove_account_from_login_at_file( // Remove the file, or write the new file if stored_wallet.is_empty() { - log::info!("Removing file: {:#?}", filepath); + log::info!("Removing file: {filepath:#?}"); Ok(fs::remove_file(filepath)?) } else { write_to_file(filepath, &stored_wallet) diff --git a/nyx-chain-watcher/build.rs b/nyx-chain-watcher/build.rs index 5cf16e56f8..a6dbb3c105 100644 --- a/nyx-chain-watcher/build.rs +++ b/nyx-chain-watcher/build.rs @@ -14,7 +14,7 @@ async fn main() -> Result<()> { } let db_path_str = db_path.display().to_string().replace('\\', "/"); - let db_url = format!("sqlite:{}", db_path_str); + let db_url = format!("sqlite:{db_path_str}"); // Ensure database file is created with proper permissions let connect_options = SqliteConnectOptions::from_str(&db_url)? @@ -30,7 +30,7 @@ async fn main() -> Result<()> { // Force SQLx to prepare all queries during build println!("cargo:rustc-env=SQLX_OFFLINE=true"); - println!("cargo:rustc-env=DATABASE_URL={}", db_url); + println!("cargo:rustc-env=DATABASE_URL={db_url}"); // Add rerun-if-changed directives println!("cargo:rerun-if-changed=migrations"); @@ -46,7 +46,7 @@ fn export_db_variables(db_url: &str) -> Result<()> { let mut file = File::create(".env")?; for (var, value) in map.iter() { - writeln!(file, "{}={}", var, value)?; + writeln!(file, "{var}={value}")?; } Ok(()) diff --git a/nyx-chain-watcher/src/cli/commands/run/mod.rs b/nyx-chain-watcher/src/cli/commands/run/mod.rs index 84d9f10f3b..150738dfb6 100644 --- a/nyx-chain-watcher/src/cli/commands/run/mod.rs +++ b/nyx-chain-watcher/src/cli/commands/run/mod.rs @@ -133,7 +133,7 @@ pub(crate) async fn execute(args: Args, http_port: u16) -> Result<(), NyxChainWa std::fs::create_dir_all(parent)?; } - let connection_url = format!("sqlite://{}?mode=rwc", db_path); + let connection_url = format!("sqlite://{db_path}?mode=rwc"); let storage = db::Storage::init(connection_url).await?; let watcher_pool = storage.pool_owned(); diff --git a/nyx-chain-watcher/src/http/server.rs b/nyx-chain-watcher/src/http/server.rs index 96e7d47f95..4216441d66 100644 --- a/nyx-chain-watcher/src/http/server.rs +++ b/nyx-chain-watcher/src/http/server.rs @@ -34,7 +34,7 @@ pub(crate) async fn build_http_api( ); let router = router_builder.with_state(state); - let bind_addr = format!("0.0.0.0:{}", http_port); + let bind_addr = format!("0.0.0.0:{http_port}"); let server = router.build_server(bind_addr).await?; Ok(server) } diff --git a/nyx-chain-watcher/src/logging.rs b/nyx-chain-watcher/src/logging.rs index 74479e3c7d..1c8cbeebb1 100644 --- a/nyx-chain-watcher/src/logging.rs +++ b/nyx-chain-watcher/src/logging.rs @@ -36,7 +36,7 @@ pub(crate) fn setup_tracing_logger() { "axum", ]; for crate_name in filter_crates { - filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))); + filter = filter.add_directive(directive_checked(format!("{crate_name}=warn"))); } log_builder.with_env_filter(filter).init(); diff --git a/sdk/rust/nym-sdk/Cargo.toml b/sdk/rust/nym-sdk/Cargo.toml index 46eb67d3ef..9c67bbf898 100644 --- a/sdk/rust/nym-sdk/Cargo.toml +++ b/sdk/rust/nym-sdk/Cargo.toml @@ -18,9 +18,9 @@ path = "src/tcp_proxy/bin/proxy_client.rs" async-trait = { workspace = true } bip39 = { workspace = true } nym-client-core = { path = "../../../common/client-core", features = [ - "fs-credentials-storage", - "fs-surb-storage", - "fs-gateways-storage", + "fs-credentials-storage", + "fs-surb-storage", + "fs-gateways-storage", ] } nym-crypto = { path = "../../../common/crypto" } nym-gateway-requests = { path = "../../../common/gateway-requests" } @@ -36,14 +36,14 @@ nym-task = { path = "../../../common/task" } nym-topology = { path = "../../../common/topology" } nym-socks5-client-core = { path = "../../../common/socks5-client-core" } nym-validator-client = { path = "../../../common/client-libs/validator-client", features = [ - "http-client", + "http-client", ] } nym-socks5-requests = { path = "../../../common/socks5/requests" } nym-ordered-buffer = { path = "../../../common/socks5/ordered-buffer" } nym-service-providers-common = { path = "../../../service-providers/common" } nym-sphinx-addressing = { path = "../../../common/nymsphinx/addressing" } nym-bin-common = { path = "../../../common/bin-common", features = [ - "basic_tracing", + "basic_tracing", ] } bytecodec = { workspace = true } httpcodec = { workspace = true } @@ -80,6 +80,7 @@ dotenvy = { workspace = true } reqwest = { workspace = true, features = ["json", "socks"] } thiserror = { workspace = true } tokio = { workspace = true, features = ["full"] } +time = { workspace = true } nym-bin-common = { path = "../../../common/bin-common", features = ["basic_tracing"] } # extra dependencies for libp2p examples diff --git a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs index 63e24f07e7..7b08e73f24 100644 --- a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs +++ b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs @@ -3,8 +3,8 @@ use nym_sdk::mixnet; use nym_sdk::mixnet::MixnetMessageSender; -use nym_topology::provider_trait::{async_trait, TopologyProvider}; -use nym_topology::{NymTopology, NymTopologyMetadata}; +use nym_topology::provider_trait::{async_trait, ToTopologyMetadata, TopologyProvider}; +use nym_topology::NymTopology; use url::Url; struct MyTopologyProvider { @@ -31,10 +31,8 @@ impl MyTopologyProvider { .await .unwrap(); - let metadata = NymTopologyMetadata::new( - mixnodes_response.metadata.rotation_id, - mixnodes_response.metadata.absolute_epoch_id, - ); + let metadata = mixnodes_response.metadata.to_topology_metadata(); + let mut base_topology = NymTopology::new(metadata, rewarded_set, Vec::new()); // in our topology provider only use mixnodes that have node_id divisible by 3 diff --git a/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs b/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs index f09ca26c9d..470fca4b83 100644 --- a/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs +++ b/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs @@ -77,7 +77,7 @@ async fn main() { let gateways = starting_topology.topology.entry_capable_nodes(); // you should have obtained valid metadata information, in particular the key rotation ID! - let metadata = NymTopologyMetadata::new(u32::MAX, 123); + let metadata = NymTopologyMetadata::new(u32::MAX, 123, time::OffsetDateTime::now_utc()); let mut custom_topology = NymTopology::new(metadata, rewarded_set, Vec::new()); custom_topology.add_routing_nodes(nodes); diff --git a/sdk/rust/nym-sdk/examples/surb_reply.rs b/sdk/rust/nym-sdk/examples/surb_reply.rs index 74f9162470..6011181f0f 100644 --- a/sdk/rust/nym-sdk/examples/surb_reply.rs +++ b/sdk/rust/nym-sdk/examples/surb_reply.rs @@ -55,8 +55,7 @@ async fn main() { // parse sender_tag: we will use this to reply to sender without needing their Nym address let return_recipient: AnonymousSenderTag = message[0].sender_tag.unwrap(); println!( - "\nReceived the following message: {} \nfrom sender with surb bucket {}", - parsed, return_recipient + "\nReceived the following message: {parsed} \nfrom sender with surb bucket {return_recipient}" ); // reply to self with it: note we use `send_str_reply` instead of `send_str` diff --git a/sdk/rust/nym-sdk/examples/tcp_proxy_single_connection.rs b/sdk/rust/nym-sdk/examples/tcp_proxy_single_connection.rs index 86cfbf70cd..adce2e263f 100644 --- a/sdk/rust/nym-sdk/examples/tcp_proxy_single_connection.rs +++ b/sdk/rust/nym-sdk/examples/tcp_proxy_single_connection.rs @@ -45,7 +45,7 @@ async fn main() -> anyhow::Result<()> { let server_port = env::args() .nth(1) .expect("Server listen port not specified"); - let upstream_tcp_addr = format!("127.0.0.1:{}", server_port); + let upstream_tcp_addr = format!("127.0.0.1:{server_port}"); // This dir gets cleaned up at the end: NOTE if you switch env between tests without letting the file do the automatic cleanup, make sure to manually remove this directory up before running again, otherwise your client will attempt to use these keys for the new env let home_dir = dirs::home_dir().expect("Unable to get home directory"); @@ -155,7 +155,7 @@ async fn main() -> anyhow::Result<()> { // The assumption regarding integration is that you know what you're sending, and will do proper // framing before and after, know what data types you're expecting, etc; the proxies are just piping bytes // back and forth using tokio's `Bytecodec` under the hood. - let local_tcp_addr = format!("127.0.0.1:{}", client_port); + let local_tcp_addr = format!("127.0.0.1:{client_port}"); let stream = TcpStream::connect(local_tcp_addr).await?; let (read, mut write) = stream.into_split(); diff --git a/sdk/rust/nym-sdk/src/client_pool/mixnet_client_pool.rs b/sdk/rust/nym-sdk/src/client_pool/mixnet_client_pool.rs index ff7b8b8198..76209b1792 100644 --- a/sdk/rust/nym-sdk/src/client_pool/mixnet_client_pool.rs +++ b/sdk/rust/nym-sdk/src/client_pool/mixnet_client_pool.rs @@ -47,7 +47,7 @@ impl fmt::Debug for ClientPool { "client_pool_reserve_number", &self.client_pool_reserve_number, ) - .field("clients", &format_args!("[{}]", clients_debug)); + .field("clients", &format_args!("[{clients_debug}]")); debug_struct.finish() } } diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index 47e958cb38..90d1b218b6 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -510,7 +510,7 @@ where Ok(active) => { if let Some(active) = active.registration { let id = active.details.gateway_id(); - debug!("currently selected gateway: {0}", id); + debug!("currently selected gateway: {id}"); } } } diff --git a/sdk/rust/nym-sdk/src/mixnet/native_client.rs b/sdk/rust/nym-sdk/src/mixnet/native_client.rs index caa0f7d3e4..fb940c5b4d 100644 --- a/sdk/rust/nym-sdk/src/mixnet/native_client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/native_client.rs @@ -226,14 +226,14 @@ impl MixnetClient { log::debug!("Sending forget me request: {:?}", self.forget_me); match self.send_forget_me().await { Ok(_) => (), - Err(e) => error!("Failed to send forget me request: {}", e), + Err(e) => error!("Failed to send forget me request: {e}"), }; tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; } else if self.remember_me.stats() { log::debug!("Sending remember me request: {:?}", self.remember_me); match self.send_remember_me().await { Ok(_) => (), - Err(e) => error!("Failed to send remember me request: {}", e), + Err(e) => error!("Failed to send remember me request: {e}"), }; tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; } @@ -255,7 +255,7 @@ impl MixnetClient { match self.client_request_sender.send(client_request).await { Ok(_) => Ok(()), Err(e) => { - error!("Failed to send forget me request: {}", e); + error!("Failed to send forget me request: {e}"); Err(Error::MessageSendingFailure) } } @@ -268,7 +268,7 @@ impl MixnetClient { match self.client_request_sender.send(client_request).await { Ok(_) => Ok(()), Err(e) => { - error!("Failed to send forget me request: {}", e); + error!("Failed to send forget me request: {e}"); Err(Error::MessageSendingFailure) } } diff --git a/service-providers/authenticator/src/cli/peer_handler.rs b/service-providers/authenticator/src/cli/peer_handler.rs index ebf34dd3b0..402c09815a 100644 --- a/service-providers/authenticator/src/cli/peer_handler.rs +++ b/service-providers/authenticator/src/cli/peer_handler.rs @@ -28,23 +28,23 @@ impl DummyHandler { if let Some(msg) = msg { match msg { PeerControlRequest::AddPeer { peer, client_id, response_tx } => { - log::info!("[DUMMY] Adding peer {:?} with client id {:?}", peer, client_id); + log::info!("[DUMMY] Adding peer {peer:?} with client id {client_id:?}"); response_tx.send(AddPeerControlResponse { success: true }).ok(); } PeerControlRequest::RemovePeer { key, response_tx } => { - log::info!("[DUMMY] Removing peer {:?}", key); + log::info!("[DUMMY] Removing peer {key:?}"); response_tx.send(RemovePeerControlResponse { success: true }).ok(); } PeerControlRequest::QueryPeer{key, response_tx} => { - log::info!("[DUMMY] Querying peer {:?}", key); + log::info!("[DUMMY] Querying peer {key:?}"); response_tx.send(QueryPeerControlResponse { success: true, peer: None }).ok(); } PeerControlRequest::QueryBandwidth{key, response_tx} => { - log::info!("[DUMMY] Querying bandwidth for peer {:?}", key); + log::info!("[DUMMY] Querying bandwidth for peer {key:?}"); response_tx.send(QueryBandwidthControlResponse { success: true, bandwidth_data: None }).ok(); } PeerControlRequest::GetClientBandwidth{key, response_tx} => { - log::info!("[DUMMY] Getting client bandwidth for peer {:?}", key); + log::info!("[DUMMY] Getting client bandwidth for peer {key:?}"); response_tx.send(GetClientBandwidthControlResponse {client_bandwidth: None }).ok(); } } diff --git a/service-providers/authenticator/src/cli/run.rs b/service-providers/authenticator/src/cli/run.rs index 67dc226523..d6b273f5b1 100644 --- a/service-providers/authenticator/src/cli/run.rs +++ b/service-providers/authenticator/src/cli/run.rs @@ -33,7 +33,7 @@ impl From for OverrideConfig { pub(crate) async fn execute(args: &Run) -> Result<(), AuthenticatorError> { let mut config = try_load_current_config(&args.common_args.id).await?; config = override_config(config, OverrideConfig::from(args.clone())); - log::debug!("Using config: {:#?}", config); + log::debug!("Using config: {config:#?}"); log::info!("Starting authenticator service provider"); let (wireguard_gateway_data, peer_rx) = WireguardGatewayData::new( diff --git a/service-providers/authenticator/src/mixnet_listener.rs b/service-providers/authenticator/src/mixnet_listener.rs index 382c0f2900..b28df22982 100644 --- a/service-providers/authenticator/src/mixnet_listener.rs +++ b/service-providers/authenticator/src/mixnet_listener.rs @@ -866,7 +866,7 @@ impl MixnetListener { } pub(crate) async fn run(mut self) -> Result<()> { - log::info!("Using authenticator version {}", CURRENT_VERSION); + log::info!("Using authenticator version {CURRENT_VERSION}"); let mut task_client = self.task_handle.fork("main_loop"); while !task_client.is_shutdown() { @@ -876,7 +876,7 @@ impl MixnetListener { }, _ = self.timeout_check_interval.next() => { if let Err(e) = self.remove_stale_registrations().await { - log::error!("Could not clear stale registrations. The registration process might get jammed soon - {:?}", e); + log::error!("Could not clear stale registrations. The registration process might get jammed soon - {e:?}"); } self.seen_credential_cache.remove_stale(); } diff --git a/service-providers/ip-packet-router/src/cli/run.rs b/service-providers/ip-packet-router/src/cli/run.rs index 3e4010cb32..bf71e79dea 100644 --- a/service-providers/ip-packet-router/src/cli/run.rs +++ b/service-providers/ip-packet-router/src/cli/run.rs @@ -24,7 +24,7 @@ impl From for OverrideConfig { pub(crate) async fn execute(args: &Run) -> Result<(), IpPacketRouterError> { let mut config = try_load_current_config(&args.common_args.id).await?; config = override_config(config, OverrideConfig::from(args.clone())); - log::debug!("Using config: {:#?}", config); + log::debug!("Using config: {config:#?}"); log::info!("Starting ip packet router service provider"); let mut server = nym_ip_packet_router::IpPacketRouter::new(config); diff --git a/service-providers/ip-packet-router/src/clients/connected_client_handler.rs b/service-providers/ip-packet-router/src/clients/connected_client_handler.rs index 23360a2edd..551518a062 100644 --- a/service-providers/ip-packet-router/src/clients/connected_client_handler.rs +++ b/service-providers/ip-packet-router/src/clients/connected_client_handler.rs @@ -69,8 +69,8 @@ impl ConnectedClientHandler { oneshot::Sender<()>, tokio::task::JoinHandle<()>, ) { - log::debug!("Starting connected client handler for: {}", client_id); - log::debug!("client version: {:?}", client_version); + log::debug!("Starting connected client handler for: {client_id}"); + log::debug!("client version: {client_version:?}"); let (close_tx, close_rx) = oneshot::channel(); let (forward_from_tun_tx, forward_from_tun_rx) = mpsc::unbounded_channel(); diff --git a/service-providers/ip-packet-router/src/messages/response/v8.rs b/service-providers/ip-packet-router/src/messages/response/v8.rs index 99b87075c8..1d0b5a3413 100644 --- a/service-providers/ip-packet-router/src/messages/response/v8.rs +++ b/service-providers/ip-packet-router/src/messages/response/v8.rs @@ -30,8 +30,7 @@ impl TryFrom for IpPacketResponseV8 { match response.response { Response::StaticConnect { .. } => { return Err(IpPacketRouterError::UnsupportedResponse(format!( - "Static connect response is not supported in version {}", - version + "Static connect response is not supported in version {version}" ))) } Response::DynamicConnect { request_id, reply } => IpPacketResponseDataV8::Control( diff --git a/service-providers/ip-packet-router/src/mixnet_listener.rs b/service-providers/ip-packet-router/src/mixnet_listener.rs index 3a35dc7ff0..fe622f8aa0 100644 --- a/service-providers/ip-packet-router/src/mixnet_listener.rs +++ b/service-providers/ip-packet-router/src/mixnet_listener.rs @@ -310,7 +310,7 @@ impl MixnetListener { // Check if the client is connected if !self.connected_clients.is_client_connected(&client_id) { - log::info!("Client {} is not connected, cannot disconnect", client_id); + log::info!("Client {client_id} is not connected, cannot disconnect"); return Ok(Some(VersionedResponse { version, reply_to: client_id, @@ -322,7 +322,7 @@ impl MixnetListener { } // Disconnect the client - log::info!("Disconnecting client {}", client_id); + log::info!("Disconnecting client {client_id}"); self.connected_clients.disconnect_client(&client_id); Ok(Some(VersionedResponse { diff --git a/service-providers/network-requester/examples/query.rs b/service-providers/network-requester/examples/query.rs index 87f410ee75..a11eeff143 100644 --- a/service-providers/network-requester/examples/query.rs +++ b/service-providers/network-requester/examples/query.rs @@ -13,7 +13,7 @@ fn parse_response(received: Vec) -> Socks5Response { assert_eq!(received.len(), 1); let response: Response = Response::try_from_bytes(&received[0].message).unwrap(); match response.content { - ResponseContent::Control(control) => panic!("unexpected control response: {:?}", control), + ResponseContent::Control(control) => panic!("unexpected control response: {control:?}"), ResponseContent::ProviderData(data) => data, } } diff --git a/service-providers/network-requester/src/cli/run.rs b/service-providers/network-requester/src/cli/run.rs index 6a061dd711..09938ac46a 100644 --- a/service-providers/network-requester/src/cli/run.rs +++ b/service-providers/network-requester/src/cli/run.rs @@ -47,7 +47,7 @@ impl From for OverrideConfig { pub(crate) async fn execute(args: &Run) -> Result<(), NetworkRequesterError> { let mut config = try_load_current_config(&args.common_args.id).await?; config = override_config(config, OverrideConfig::from(args.clone())); - log::debug!("Using config: {:#?}", config); + log::debug!("Using config: {config:#?}"); if config.network_requester.open_proxy { println!( diff --git a/tools/echo-server/src/lib.rs b/tools/echo-server/src/lib.rs index 0bea93869a..4630e979dd 100644 --- a/tools/echo-server/src/lib.rs +++ b/tools/echo-server/src/lib.rs @@ -61,7 +61,7 @@ impl NymEchoServer { let home_dir = dirs::home_dir().expect("Unable to get home directory"); let default_path = format!("{}/tmp/nym-proxy-server-config", home_dir.display()); let config_path = config_path.unwrap_or(&default_path); - let listen_addr = format!("127.0.0.1:{}", listen_port); + let listen_addr = format!("127.0.0.1:{listen_port}"); let client = Arc::new(Mutex::new( tcp_proxy::NymProxyServer::new(&listen_addr, config_path, env, gateway).await?, @@ -337,7 +337,7 @@ mod tests { ); let coded_message = bincode::serialize(&outgoing)?; - println!("sending {:?}", coded_message); + println!("sending {coded_message:?}"); let mut client = MixnetClient::connect_new().await?; diff --git a/tools/internal/testnet-manager/Cargo.toml b/tools/internal/testnet-manager/Cargo.toml index d5384a9953..4da0379b11 100644 --- a/tools/internal/testnet-manager/Cargo.toml +++ b/tools/internal/testnet-manager/Cargo.toml @@ -16,6 +16,7 @@ console = { workspace = true } cw-utils.workspace = true clap = { workspace = true, features = ["cargo", "derive"] } indicatif = { workspace = true } +humantime = { workspace = true } rand.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true diff --git a/tools/internal/testnet-manager/build.rs b/tools/internal/testnet-manager/build.rs index cdd97f9505..7e969e1e08 100644 --- a/tools/internal/testnet-manager/build.rs +++ b/tools/internal/testnet-manager/build.rs @@ -4,9 +4,9 @@ use std::env; #[tokio::main] async fn main() { let out_dir = env::var("OUT_DIR").unwrap(); - let database_path = format!("{}/nym-api-example.sqlite", out_dir); + let database_path = format!("{out_dir}/nym-api-example.sqlite"); - let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path)) + let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc")) .await .expect("Failed to create SQLx database connection"); diff --git a/tools/internal/testnet-manager/src/error.rs b/tools/internal/testnet-manager/src/error.rs index d85d8c7dcb..42dd29b2d6 100644 --- a/tools/internal/testnet-manager/src/error.rs +++ b/tools/internal/testnet-manager/src/error.rs @@ -103,4 +103,7 @@ pub enum NetworkManagerError { #[error("timed out while waiting for the gateway to start receiving traffic (you need to actually run it!)")] GatewayWaitTimeout, + + #[error("attempted to bond nodes on a non-empty network")] + NetworkNotEmpty, } diff --git a/tools/internal/testnet-manager/src/manager/local_nodes.rs b/tools/internal/testnet-manager/src/manager/local_nodes.rs index 530ed31588..e029e9f0f4 100644 --- a/tools/internal/testnet-manager/src/manager/local_nodes.rs +++ b/tools/internal/testnet-manager/src/manager/local_nodes.rs @@ -10,14 +10,18 @@ use console::style; use nym_crypto::asymmetric::ed25519; use nym_mixnet_contract_common::nym_node::Role; use nym_mixnet_contract_common::RoleAssignment; -use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::{ + MixnetQueryClient, MixnetSigningClient, PagedMixnetQueryClient, +}; use nym_validator_client::DirectSigningHttpRpcNyxdClient; use serde::{Deserialize, Serialize}; use std::fs; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process::Stdio; +use time::OffsetDateTime; use tokio::process::Command; +use tokio::time::sleep; use tracing::error; use zeroize::Zeroizing; @@ -89,6 +93,16 @@ impl<'a> LocalNodesCtx<'a> { .clone(), )?) } + + fn signing_mixnet_contract_admin( + &self, + ) -> Result { + Ok(DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + self.network.client_config()?, + self.network.rpc_endpoint.as_str(), + self.network.contracts.mixnet.admin_mnemonic.clone(), + )?) + } } #[derive(Debug, Deserialize, Serialize)] @@ -227,6 +241,40 @@ impl NetworkManager { Ok(()) } + async fn check_if_network_is_empty( + &self, + ctx: &LocalNodesCtx<'_>, + ) -> Result<(), NetworkManagerError> { + ctx.println(format!( + "🐽 {}Making sure the network is fresh...", + style("[0/5]").bold().dim() + )); + + ctx.set_pb_message("checking network state..."); + + let client = ctx.signing_mixnet_contract_admin()?; + let fut = client.get_all_nymnode_bonds(); + let nym_nodes = ctx.async_with_progress(fut).await?; + + if !nym_nodes.is_empty() { + return Err(NetworkManagerError::NetworkNotEmpty); + } + + let fut = client.get_all_mixnode_bonds(); + let mixnodes = ctx.async_with_progress(fut).await?; + if !mixnodes.is_empty() { + return Err(NetworkManagerError::NetworkNotEmpty); + } + + let fut = client.get_all_gateways(); + let gateways = ctx.async_with_progress(fut).await?; + if !gateways.is_empty() { + return Err(NetworkManagerError::NetworkNotEmpty); + } + + Ok(()) + } + async fn initialise_nym_nodes( &self, ctx: &mut LocalNodesCtx<'_>, @@ -353,6 +401,75 @@ impl NetworkManager { // this could be batched in a single tx, but that's too much effort for now let rewarder = ctx.signing_rewarder()?; + ctx.set_pb_message("checking and temporarily adjusting epoch lengths..."); + let fut = rewarder.get_current_interval_details(); + let original_epoch = ctx.async_with_progress(fut).await?; + + let expected_end = original_epoch.interval.current_epoch_end(); + let now = OffsetDateTime::now_utc(); + if expected_end > now { + loop { + let now = OffsetDateTime::now_utc(); + let diff = expected_end - now; + if diff.is_negative() { + break; + } + + let std_diff = diff.unsigned_abs(); + let fut = sleep(std::time::Duration::from_millis(500)); + ctx.set_pb_message(format!( + "waiting for {} for the epoch end...", + humantime::format_duration(std_diff) + )); + ctx.async_with_progress(fut).await; + } + // wait extra 10s due to possible block time desync + ctx.set_pb_message("waiting extra 10s to make sure blocks have advanced".to_string()); + let fut = sleep(std::time::Duration::from_secs(10)); + ctx.async_with_progress(fut).await; + } + + // TODO: for some reason contract rejects correct admin. won't be debugging it now. + // let changed_length = if expected_end > now { + // + // // if it's < 10s, just wait + // let diff = expected_end - now; + // + // if diff < Duration::seconds(10) { + // let std_diff = diff.unsigned_abs(); + // let fut = sleep(std_diff); + // ctx.set_pb_message(format!( + // "waiting for {} for the epoch end...", + // humantime::format_duration(std_diff) + // )); + // ctx.async_with_progress(fut).await; + // false + // } else { + // ctx.println(format!( + // "🙈 {}Reducing epoch length...", + // style("[4.pre/5]").bold().dim() + // )); + // + // // just lower the epoch length and later restore it + // let admin = ctx.signing_mixnet_contract_admin()?; + // let fut = admin.update_interval_config( + // original_epoch.interval.epochs_in_interval(), + // 10, + // true, + // None, + // ); + // ctx.async_with_progress(fut).await?; + // let fut = sleep(std::time::Duration::from_secs(10)); + // ctx.set_pb_message("waiting for 10s for the epoch end..."); + // ctx.async_with_progress(fut).await; + // true + // } + // } else { + // false + // }; + + // reduce epoch length if it would prevent us from the advancing the state + ctx.set_pb_message("starting epoch transition..."); let fut = rewarder.begin_epoch_transition(None); ctx.async_with_progress(fut).await?; @@ -421,6 +538,23 @@ impl NetworkManager { ); ctx.async_with_progress(fut).await?; + // TODO: for some reason contract rejects correct admin. won't be debugging it now. + // if changed_length { + // ctx.println(format!( + // "🙈 {}Restoring epoch length...", + // style("[4.post/5]").bold().dim() + // )); + // ctx.set_pb_message("restoring original epoch length..."); + // let admin = ctx.signing_mixnet_contract_admin()?; + // let fut = admin.update_interval_config( + // original_epoch.interval.epochs_in_interval(), + // original_epoch.interval.epoch_length_secs(), + // true, + // None, + // ); + // ctx.async_with_progress(fut).await?; + // } + Ok(()) } @@ -507,6 +641,7 @@ impl NetworkManager { return Err(NetworkManagerError::EnvFileNotGenerated); } + self.check_if_network_is_empty(&ctx).await?; self.initialise_nym_nodes(&mut ctx, mixnodes, gateways) .await?; self.transfer_bonding_tokens(&ctx).await?; diff --git a/tools/nym-cli/src/main.rs b/tools/nym-cli/src/main.rs index 9fa5a3d9dd..b8315de641 100644 --- a/tools/nym-cli/src/main.rs +++ b/tools/nym-cli/src/main.rs @@ -138,10 +138,7 @@ async fn execute(cli: Cli) -> anyhow::Result<()> { async fn wait_for_interrupt() { if let Err(e) = tokio::signal::ctrl_c().await { - error!( - "There was an error while capturing SIGINT - {:?}. We will terminate regardless", - e - ); + error!("There was an error while capturing SIGINT - {e:?}. We will terminate regardless",); } println!( "Received SIGINT - the process will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)." diff --git a/tools/nym-nr-query/src/main.rs b/tools/nym-nr-query/src/main.rs index 67980d638f..aecf7110d0 100644 --- a/tools/nym-nr-query/src/main.rs +++ b/tools/nym-nr-query/src/main.rs @@ -75,7 +75,7 @@ fn parse_socks5_response(received: Vec) -> Socks5R assert_eq!(received.len(), 1); let response: Response = Response::try_from_bytes(&received[0].message).unwrap(); match response.content { - ResponseContent::Control(control) => panic!("unexpected control response: {:?}", control), + ResponseContent::Control(control) => panic!("unexpected control response: {control:?}"), ResponseContent::ProviderData(data) => data, } } @@ -276,9 +276,9 @@ enum ClientResponse { impl fmt::Display for ClientResponse { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ClientResponse::Control(control) => write!(f, "{:#?}", control), - ClientResponse::Query(query) => write!(f, "{:#?}", query), - ClientResponse::Ping(ping) => write!(f, "{}", ping), + ClientResponse::Control(control) => write!(f, "{control:#?}"), + ClientResponse::Query(query) => write!(f, "{query:#?}"), + ClientResponse::Ping(ping) => write!(f, "{ping}"), } } } diff --git a/wasm/client/src/encoded_payload_helper.rs b/wasm/client/src/encoded_payload_helper.rs index 71600ed5ce..8f2099425f 100644 --- a/wasm/client/src/encoded_payload_helper.rs +++ b/wasm/client/src/encoded_payload_helper.rs @@ -61,7 +61,7 @@ pub fn encode_payload_with_headers( Ok([size, metadata, payload].concat()) } Err(e) => Err(JsValue::from(JsError::new( - format!("Could not encode message: {}", e).as_str(), + format!("Could not encode message: {e}").as_str(), ))), } } @@ -84,7 +84,7 @@ pub fn decode_payload(message: Vec) -> Result { .unwrap() .unchecked_into::()), Err(e) => Err(JsValue::from(JsError::new( - format!("Could not parse message: {}", e).as_str(), + format!("Could not parse message: {e}").as_str(), ))), } }