Compare commits

..

7 Commits

Author SHA1 Message Date
Jon Häggblad 4d09389c18 WIP 2022-12-14 19:54:45 +01:00
Jon Häggblad eb07ec8580 client: sort out shutdown procedure and harmonize with socks5-client (#2695)
* common/task: rename ShutdownNotifier to TaskManager

* nym-client: return boxed error

* nym-client: enable graceful shutdown

* nym-client: task wait on shutdown to instead exit on closed channel

* Fix build

* Fix unused

* changelog: update
2022-12-14 17:13:00 +01:00
Dave Hrycyszyn f6a79ce7c3 Renaming validator-api to nym-api (#1863)
* Renaming validator-api to nym-api

* nym-api: simplified crate name

* Added nym-api rename to changelog

* Changed some output messages

* Renamed validator-api-requests to nym-api requests

* Removing more references to validator-api-requests

* Additional lockfile name changes after full build

* Removing mistakenly added merge files

* ibid

* ibid

* Getting rid of ref to validator_api deep inside validator-client

* Fixing file storage paths

* Renaming struct function names referring to validator_api

* Simplifying struct init

* Fixed up all other instances of nym_api.

* Renaming validatorApi to nymApi in TypeScript client for consistency

v

* Found a few more Rust instances

* Changed examples in TypeScript SDK

* Found one more instance of the use of validator instead of nym apis

* Aliasing config key name for deserialization to preserve compatibility with old configs
2022-12-14 15:05:01 +00:00
Fouad 4ece8b7e8f Feature/nym connect experimental software text (#2692)
* use experimental text
2022-12-14 13:04:54 +00:00
Bogdan-Ștefan Neacşu bb557985c0 DKG resharing unit test (#2668) 2022-12-14 14:47:59 +02:00
Fouad 3ebb24b2c8 get version number from tauri and display (#2684)
* get version number from tauri and display

* update internal version numbers
2022-12-14 12:12:34 +00:00
Jędrzej Stuczyński 97b01db23e Chore/more error macros (#2686)
* cleaned up MixProcessingError

* Added Error impl to (hopefully) all error enums in the codebase

* Replaced all occurences of error("{0}") with error(transparent)

* Changelog entry
2022-12-13 17:42:11 +00:00
430 changed files with 2384 additions and 12174 deletions
+5 -13
View File
@@ -4,21 +4,13 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [v1.1.4] (2022-12-20)
This release adds multiple Single Use Reply Blocks (SURBs) to allow arbitrarily-sized anonymized replies.
At the moment this is turned off by default, but available for use by application developers.
We will need to wait for network-requesters to upgrade to this new release, after which multi-SURB anonymization will become the default setting for the SOCKS proxy clients.
The release also include some additional work for distributed key generation in the Coconut signing authority nodes.
### Changed
- Feature/dkg contract threshold by @neacsu in https://github.com/nymtech/nym/pull/1885
- Multi-surbs by @jstuczyn in https://github.com/nymtech/nym/pull/2667
- Fix multi-surb backwards compatibility in pre 1.1.4 client config files by @jstuczyn in https://github.com/nymtech/nym/pull/2703
- fix: ignore corrupted surb storage and instead create fresh one by @jstuczyn in https://github.com/nymtech/nym/pull/2711
- socks5: rework waiting in inbound.rs by @octol in https://github.com/nymtech/nym/pull/1880
- all-binaries: improved error logging ([#2686])
- native client: bring shutdown logic up to the same level as socks5-client
[#2686]: https://github.com/nymtech/nym/pull/2686
## [v1.1.3] (2022-12-13)
Generated
+35 -29
View File
@@ -586,7 +586,7 @@ dependencies = [
[[package]]
name = "client-core"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"async-trait",
"client-connections",
@@ -967,9 +967,9 @@ dependencies = [
"coconut-interface",
"cosmrs",
"crypto",
"nym-api-requests",
"rand 0.7.3",
"thiserror",
"validator-api-requests",
"validator-client",
]
@@ -1116,6 +1116,7 @@ dependencies = [
"serde",
"serde_bytes",
"subtle-encoding",
"thiserror",
"x25519-dalek",
]
@@ -2934,6 +2935,7 @@ dependencies = [
"rand 0.8.5",
"serde",
"task",
"thiserror",
"tokio",
"tokio-util 0.7.3",
"url",
@@ -3089,6 +3091,21 @@ dependencies = [
"libc",
]
[[package]]
name = "nym-api-requests"
version = "0.1.0"
dependencies = [
"bs58",
"coconut-interface",
"cosmrs",
"cosmwasm-std",
"getset",
"mixnet-contract-common",
"schemars",
"serde",
"ts-rs",
]
[[package]]
name = "nym-bity-integration"
version = "0.1.0"
@@ -3106,7 +3123,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"anyhow",
"base64",
@@ -3160,7 +3177,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"clap 3.2.8",
"client-connections",
@@ -3199,7 +3216,7 @@ dependencies = [
[[package]]
name = "nym-gateway"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"anyhow",
"async-trait",
@@ -3223,6 +3240,7 @@ dependencies = [
"mixnet-client",
"mixnode-common",
"network-defaults",
"nym-api-requests",
"nymsphinx",
"once_cell",
"pemstore",
@@ -3238,7 +3256,6 @@ dependencies = [
"tokio-tungstenite 0.14.0",
"tokio-util 0.7.3",
"url",
"validator-api-requests",
"validator-client",
"vergen 5.1.17",
"version-checker",
@@ -3246,7 +3263,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"anyhow",
"bs58",
@@ -3288,7 +3305,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"async-trait",
"clap 3.2.8",
@@ -3320,7 +3337,7 @@ dependencies = [
[[package]]
name = "nym-network-statistics"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"dirs",
"log",
@@ -3336,7 +3353,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"clap 3.2.8",
"client-connections",
@@ -3403,7 +3420,7 @@ dependencies = [
[[package]]
name = "nym-validator-api"
version = "1.1.4"
version = "1.1.3"
dependencies = [
"anyhow",
"async-trait",
@@ -3434,6 +3451,7 @@ dependencies = [
"logging",
"mixnet-contract-common",
"multisig-contract-common",
"nym-api-requests",
"nymcoconut",
"nymsphinx",
"okapi",
@@ -3459,7 +3477,6 @@ dependencies = [
"topology",
"ts-rs",
"url",
"validator-api-requests",
"validator-client",
"vergen 7.2.1",
"version-checker",
@@ -3541,6 +3558,7 @@ dependencies = [
"nymsphinx-types",
"pemstore",
"rand 0.7.3",
"thiserror",
"topology",
]
@@ -3615,6 +3633,7 @@ dependencies = [
"bytes",
"nymsphinx-params",
"nymsphinx-types",
"thiserror",
"tokio-util 0.7.3",
]
@@ -3624,6 +3643,7 @@ version = "0.1.0"
dependencies = [
"crypto",
"nymsphinx-types",
"thiserror",
]
[[package]]
@@ -3707,6 +3727,7 @@ name = "ordered-buffer"
version = "0.1.0"
dependencies = [
"log",
"thiserror",
]
[[package]]
@@ -6189,10 +6210,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"mixnet-contract-common",
"nym-api-requests",
"nym-types",
"nym-wallet-types",
"ts-rs",
"validator-api-requests",
"validator-client",
"vesting-contract-common",
"walkdir",
@@ -6398,21 +6419,6 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
[[package]]
name = "validator-api-requests"
version = "0.1.0"
dependencies = [
"bs58",
"coconut-interface",
"cosmrs",
"cosmwasm-std",
"getset",
"mixnet-contract-common",
"schemars",
"serde",
"ts-rs",
]
[[package]]
name = "validator-client"
version = "0.1.0"
@@ -6437,6 +6443,7 @@ dependencies = [
"mixnet-contract-common",
"multisig-contract-common",
"network-defaults",
"nym-api-requests",
"prost 0.10.3",
"reqwest",
"serde",
@@ -6446,7 +6453,6 @@ dependencies = [
"tokio",
"ts-rs",
"url",
"validator-api-requests",
"vesting-contract",
"vesting-contract-common",
]
+3 -3
View File
@@ -74,8 +74,8 @@ members = [
"mixnode",
"service-providers/network-requester",
"service-providers/network-statistics",
"validator-api",
"validator-api/validator-api-requests",
"nym-api",
"nym-api/nym-api-requests",
"tools/nym-cli",
"tools/ts-rs-cli"
]
@@ -87,7 +87,7 @@ default-members = [
"service-providers/network-requester",
"service-providers/network-statistics",
"mixnode",
"validator-api",
"nym-api",
"explorer-api",
]
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "client-core"
version = "1.1.4"
version = "1.1.3"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
@@ -36,19 +36,19 @@ use nymsphinx::addressing::nodes::NodeIdentity;
use std::sync::Arc;
use std::time::Duration;
use tap::TapFallible;
use task::{ShutdownListener, ShutdownNotifier};
use task::{TaskClient, TaskManager};
use url::Url;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
pub mod non_wasm_helpers;
pub struct ClientInput {
pub shared_lane_queue_lengths: LaneQueueLengths,
pub connection_command_sender: ConnectionCommandSender,
pub input_sender: InputMessageSender,
}
pub struct ClientOutput {
pub shared_lane_queue_lengths: LaneQueueLengths,
pub received_buffer_request_sender: ReceivedBufferRequestSender,
}
@@ -85,7 +85,7 @@ pub struct BaseClientBuilder<'a, B> {
gateway_config: &'a GatewayEndpointConfig,
debug_config: &'a DebugConfig,
disabled_credentials: bool,
validator_api_endpoints: Vec<Url>,
nym_api_endpoints: Vec<Url>,
reply_storage_backend: B,
bandwidth_controller: Option<BandwidthController>,
@@ -106,7 +106,7 @@ where
gateway_config: base_config.get_gateway_endpoint_config(),
debug_config: base_config.get_debug_config(),
disabled_credentials: base_config.get_disabled_credentials_mode(),
validator_api_endpoints: base_config.get_validator_api_endpoints(),
nym_api_endpoints: base_config.get_nym_api_endpoints(),
bandwidth_controller,
reply_storage_backend,
key_manager,
@@ -120,15 +120,15 @@ where
bandwidth_controller: Option<BandwidthController>,
reply_storage_backend: B,
disabled_credentials: bool,
validator_api_endpoints: Vec<Url>,
nym_api_endpoints: Vec<Url>,
) -> BaseClientBuilder<'a, B> {
BaseClientBuilder {
gateway_config,
debug_config,
disabled_credentials,
validator_api_endpoints,
bandwidth_controller,
nym_api_endpoints,
reply_storage_backend,
bandwidth_controller,
key_manager,
}
}
@@ -151,7 +151,7 @@ where
self_address: Recipient,
topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender,
shutdown: ShutdownListener,
shutdown: TaskClient,
) {
info!("Starting loop cover traffic stream...");
@@ -185,7 +185,7 @@ where
reply_controller_receiver: ReplyControllerReceiver,
lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver,
shutdown: ShutdownListener,
shutdown: TaskClient,
) {
info!("Starting real traffic stream...");
@@ -212,7 +212,7 @@ where
mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender,
shutdown: ShutdownListener,
shutdown: TaskClient,
) {
info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new(
@@ -229,7 +229,7 @@ where
&mut self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> Result<GatewayClient, ClientCoreError<B>> {
let gateway_id = self.gateway_config.gateway_id.clone();
if gateway_id.is_empty() {
@@ -281,13 +281,13 @@ where
// future responsible for periodically polling directory server and updating
// the current global view of topology
async fn start_topology_refresher(
validator_api_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
refresh_rate: Duration,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> Result<(), ClientCoreError<B>> {
let topology_refresher_config = TopologyRefresherConfig::new(
validator_api_urls,
nym_api_urls,
refresh_rate,
env!("CARGO_PKG_VERSION").to_string(),
);
@@ -317,7 +317,7 @@ where
// requests?
fn start_mix_traffic_controller(
gateway_client: GatewayClient,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> BatchMixMessageSender {
info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_client);
@@ -327,7 +327,7 @@ where
async fn setup_persistent_reply_storage(
backend: B,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> Result<CombinedReplyStorage, ClientCoreError<B>> {
let persistent_storage = PersistentReplyStorage::new(backend);
let mem_store = persistent_storage
@@ -367,7 +367,7 @@ where
let shared_topology_accessor = TopologyAccessor::new();
// Shutdown notifier for signalling tasks to stop
let shutdown = ShutdownNotifier::default();
let task_manager = TaskManager::default();
// channels responsible for dealing with reply-related fun
let (reply_controller_sender, reply_controller_receiver) =
@@ -378,18 +378,20 @@ where
// the components are started in very specific order. Unless you know what you are doing,
// do not change that.
let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe())
.start_gateway_client(mixnet_messages_sender, ack_sender, task_manager.subscribe())
.await?;
let reply_storage =
Self::setup_persistent_reply_storage(self.reply_storage_backend, shutdown.subscribe())
.await?;
let reply_storage = Self::setup_persistent_reply_storage(
self.reply_storage_backend,
task_manager.subscribe(),
)
.await?;
Self::start_topology_refresher(
self.validator_api_endpoints.clone(),
self.nym_api_endpoints.clone(),
self.debug_config.topology_refresh_rate,
shared_topology_accessor.clone(),
shutdown.subscribe(),
task_manager.subscribe(),
)
.await?;
@@ -399,7 +401,7 @@ where
mixnet_messages_receiver,
reply_storage.key_storage(),
reply_controller_sender.clone(),
shutdown.subscribe(),
task_manager.subscribe(),
);
// The sphinx_message_sender is the transmitter for any component generating sphinx packets
@@ -407,7 +409,7 @@ where
// traffic stream.
// The MixTrafficController then sends the actual traffic
let sphinx_message_sender =
Self::start_mix_traffic_controller(gateway_client, shutdown.subscribe());
Self::start_mix_traffic_controller(gateway_client, task_manager.subscribe());
// Channels that the websocket listener can use to signal downstream to the real traffic
// controller that connections are closed.
@@ -439,7 +441,7 @@ where
reply_controller_receiver,
shared_lane_queue_lengths.clone(),
client_connection_rx,
shutdown.subscribe(),
task_manager.subscribe(),
);
if !self.debug_config.disable_loop_cover_traffic_stream {
@@ -449,7 +451,7 @@ where
self_address,
shared_topology_accessor,
sphinx_message_sender,
shutdown.subscribe(),
task_manager.subscribe(),
);
}
@@ -459,17 +461,17 @@ where
Ok(BaseClient {
client_input: ClientInputStatus::AwaitingProducer {
client_input: ClientInput {
shared_lane_queue_lengths,
connection_command_sender: client_connection_tx,
input_sender,
},
},
client_output: ClientOutputStatus::AwaitingConsumer {
client_output: ClientOutput {
shared_lane_queue_lengths,
received_buffer_request_sender,
},
},
shutdown_notifier: shutdown,
task_manager,
})
}
}
@@ -478,5 +480,5 @@ pub struct BaseClient {
pub client_input: ClientInputStatus,
pub client_output: ClientOutputStatus,
pub shutdown_notifier: ShutdownNotifier,
pub task_manager: TaskManager,
}
@@ -8,57 +8,6 @@ use crate::config::DebugConfig;
use crate::error::ClientCoreError;
use log::{error, info};
use std::path::Path;
use std::{fs, io};
use time::OffsetDateTime;
async fn setup_fresh_backend<P: AsRef<Path>>(
db_path: P,
debug_config: &DebugConfig,
) -> Result<fs_backend::Backend, ClientCoreError<fs_backend::Backend>> {
info!("creating fresh surb database");
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
Ok(backend) => backend,
Err(err) => {
error!("failed to setup persistent storage backend for our reply needs: {err}");
return Err(ClientCoreError::SurbStorageError { source: err });
}
};
// while I kinda hate that we're going to be creating `CombinedReplyStorage` twice,
// it will only be happening on the very first run and in practice won't incur huge
// costs since the storage is going to be empty
let mem_store = CombinedReplyStorage::new(
debug_config.minimum_reply_surb_storage_threshold,
debug_config.maximum_reply_surb_storage_threshold,
);
storage_backend
.init_fresh(&mem_store)
.await
.map_err(|err| ClientCoreError::SurbStorageError { source: err })?;
Ok(storage_backend)
}
fn archive_corrupted_database<P: AsRef<Path>>(db_path: P) -> io::Result<()> {
let db_path = db_path.as_ref();
debug_assert!(db_path.exists());
let now = OffsetDateTime::now_utc().unix_timestamp();
let suffix = format!("_{now}.corrupted");
let new_extension =
if let Some(existing_extension) = db_path.extension().and_then(|ext| ext.to_str()) {
format!("{existing_extension}.{}", suffix)
} else {
suffix
};
let mut renamed = db_path.to_owned();
renamed.set_extension(new_extension);
fs::rename(db_path, renamed)
}
pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
db_path: P,
@@ -71,13 +20,32 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
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");
archive_corrupted_database(db_path)?;
setup_fresh_backend(db_path, debug_config).await
error!("failed to setup persistent storage backend for our reply needs: {err}");
Err(ClientCoreError::SurbStorageError { source: err })
}
}
} else {
setup_fresh_backend(db_path, debug_config).await
info!("creating fresh surb database");
let mut storage_backend = match fs_backend::Backend::init(db_path).await {
Ok(backend) => backend,
Err(err) => {
error!("failed to setup persistent storage backend for our reply needs: {err}");
return Err(ClientCoreError::SurbStorageError { source: err });
}
};
// while I kinda hate that we're going to be creating `CombinedReplyStorage` twice,
// it will only be happening on the very first run and in practice won't incur huge
// costs since the storage is going to be empty
let mem_store = CombinedReplyStorage::new(
debug_config.minimum_reply_surb_storage_threshold,
debug_config.maximum_reply_surb_storage_threshold,
);
storage_backend
.init_fresh(&mem_store)
.await
.map_err(|err| ClientCoreError::SurbStorageError { source: err })?;
Ok(storage_backend)
}
}
@@ -213,7 +213,7 @@ impl LoopCoverTrafficStream<OsRng> {
tokio::task::yield_now().await;
}
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) {
// we should set initial delay only when we actually start the stream
let sampled =
sample_poisson_duration(&mut self.rng, self.average_cover_message_sending_delay);
@@ -51,8 +51,8 @@ impl MixTrafficController {
};
match result {
Err(e) => {
error!("Failed to send sphinx packet(s) to the gateway! - {:?}", e);
Err(err) => {
error!("Failed to send sphinx packet(s) to the gateway! - {err}");
self.consecutive_gateway_failure_count += 1;
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
// todo: in the future this should initiate a 'graceful' shutdown or try
@@ -67,11 +67,11 @@ impl MixTrafficController {
}
}
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) {
spawn_future(async move {
debug!("Started MixTrafficController with graceful shutdown support");
while !shutdown.is_shutdown() {
loop {
tokio::select! {
mix_packets = self.mix_rx.recv() => match mix_packets {
Some(mix_packets) => {
@@ -82,8 +82,9 @@ impl MixTrafficController {
break;
}
},
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("MixTrafficController: Received shutdown");
break;
}
}
}
@@ -65,7 +65,7 @@ impl AcknowledgementListener {
}
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started AcknowledgementListener with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -77,7 +77,7 @@ impl AcknowledgementListener {
break;
}
},
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("AcknowledgementListener: Received shutdown");
}
}
@@ -249,7 +249,7 @@ impl ActionController {
}
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started ActionController with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -270,7 +270,7 @@ impl ActionController {
break;
}
},
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("ActionController: Received shutdown");
}
}
@@ -109,7 +109,7 @@ where
};
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started InputMessageListener with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -123,7 +123,7 @@ where
break;
}
},
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("InputMessageListener: Received shutdown");
}
}
@@ -249,7 +249,7 @@ where
}
}
pub(super) fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
pub(super) fn start_with_shutdown(self, shutdown: task::TaskClient) {
let mut acknowledgement_listener = self.acknowledgement_listener;
let mut input_message_listener = self.input_message_listener;
let mut retransmission_request_listener = self.retransmission_request_listener;
@@ -137,7 +137,7 @@ where
.await
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started RetransmissionRequestListener with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -149,7 +149,7 @@ where
break;
}
},
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("RetransmissionRequestListener: Received shutdown");
}
}
@@ -37,7 +37,7 @@ impl SentNotificationListener {
.unwrap();
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started SentNotificationListener with graceful shutdown support");
while !shutdown.is_shutdown() {
@@ -51,7 +51,7 @@ impl SentNotificationListener {
break;
}
},
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("SentNotificationListener: Received shutdown");
}
}
@@ -263,7 +263,7 @@ impl RealMessagesController<OsRng> {
}
}
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
pub fn start_with_shutdown(self, shutdown: task::TaskClient) {
let mut out_queue_control = self.out_queue_control;
let ack_control = self.ack_control;
let mut reply_control = self.reply_control;
@@ -264,7 +264,7 @@ where
};
if let Err(err) = self.mix_tx.send(vec![next_message]).await {
log::error!("Failed to send: {}", err);
log::error!("Failed to send: {err}");
}
// notify ack controller about sending our message only after we actually managed to push it
@@ -384,7 +384,10 @@ where
match Pin::new(&mut self.real_receiver).poll_recv(cx) {
// in the case our real message channel stream was closed, we should also indicate we are closed
// (and whoever is using the stream should panic)
Poll::Ready(None) => Poll::Ready(None),
Poll::Ready(None) => {
log::error!("real receiver ready(none)");
Poll::Ready(None)
}
Poll::Ready(Some((real_messages, conn_id))) => {
log::trace!("handling real_messages: size: {}", real_messages.len());
@@ -469,7 +472,7 @@ where
}
#[cfg(not(target_arch = "wasm32"))]
fn log_status(&self, shutdown: &mut task::ShutdownListener) {
fn log_status(&self, shutdown: &mut task::TaskClient) {
use crate::error::ClientCoreStatusMessage;
let packets = self.transmission_buffer.total_size();
@@ -514,7 +517,7 @@ where
}
}
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started OutQueueControl with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))]
@@ -525,7 +528,7 @@ where
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("OutQueueControl: Received shutdown");
}
_ = status_timer.tick() => {
@@ -77,10 +77,12 @@ impl SendingDelayController {
self.current_multiplier
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn min_multiplier(&self) -> u32 {
self.lower_bound
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn max_multiplier(&self) -> u32 {
self.upper_bound
}
@@ -399,21 +399,20 @@ impl RequestReceiver {
}
}
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started RequestReceiver with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("RequestReceiver: Received shutdown");
}
request = self.query_receiver.next() => {
match request {
Some(message) => self.handle_message(message).await,
None => {
log::trace!("RequestReceiver: Stopping since channel closed");
break;
},
if let Some(message) = request {
self.handle_message(message).await
} else {
log::trace!("RequestReceiver: Stopping since channel closed");
break;
}
},
}
@@ -439,20 +438,19 @@ impl FragmentedMessageReceiver {
}
}
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started FragmentedMessageReceiver with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
new_messages = self.mixnet_packet_receiver.next() => match new_messages {
Some(new_messages) => {
new_messages = self.mixnet_packet_receiver.next() => {
if let Some(new_messages) = new_messages {
self.received_buffer.handle_new_received(new_messages).await;
}
None => {
} else {
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
break;
}
},
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("FragmentedMessageReceiver: Received shutdown");
}
}
@@ -490,7 +488,7 @@ impl ReceivedMessagesBufferController {
}
}
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
pub fn start_with_shutdown(self, shutdown: task::TaskClient) {
let mut fragmented_message_receiver = self.fragmented_message_receiver;
let mut request_receiver = self.request_receiver;
@@ -891,7 +891,7 @@ where
return gloo_timers::future::IntervalStream::new(polling_rate.as_millis() as u32);
}
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: task::TaskClient) {
debug!("Started ReplyController with graceful shutdown support");
let polling_rate = Duration::from_secs(5);
@@ -904,7 +904,7 @@ where
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("ReplyController: Received shutdown");
},
req = self.request_receiver.next() => match req {
@@ -1,6 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use self::error::StorageError;
use crate::client::replies::reply_storage::backend::fs_backend::manager::StorageManager;
use crate::client::replies::reply_storage::backend::fs_backend::models::{
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
@@ -16,8 +17,6 @@ use std::fs;
use std::path::{Path, PathBuf};
use time::OffsetDateTime;
pub use self::error::StorageError;
mod error;
mod manager;
mod models;
@@ -67,12 +66,6 @@ impl Backend {
return Err(StorageError::IncompleteDataFlush);
}
let last_flush_timestamp = manager.get_previous_flush_timestamp().await?;
if last_flush_timestamp == 0 {
// either this client has been running since 1970 or the flush failed
return Err(StorageError::IncompleteDataFlush);
}
// the process has gone down without full graceful shutdown,
// meaning the database doesn't contain valid data anymore
// so we have to purge it
@@ -82,12 +75,7 @@ impl Backend {
manager.delete_all_reply_keys().await?;
}
if let Err(err) = manager.get_reply_surb_storage_metadata().await {
// we can't recover here, we HAVE TO initialise fresh (because we don't know correct starting metadata)
error!("it seems the client has been shutdown gracefully - we're missing valid surb data dump. the existing database cannot be used");
return Err(err.into());
}
let last_flush_timestamp = manager.get_previous_flush_timestamp().await?;
let last_flush = match OffsetDateTime::from_unix_timestamp(last_flush_timestamp) {
Ok(last_flush) => last_flush,
Err(err) => {
@@ -37,7 +37,7 @@ where
pub async fn flush_on_shutdown(
mut self,
mem_state: CombinedReplyStorage,
mut shutdown: task::ShutdownListener,
mut shutdown: task::TaskClient,
) {
use log::{debug, error, info, warn};
@@ -136,19 +136,15 @@ impl Default for TopologyAccessor {
}
pub struct TopologyRefresherConfig {
validator_api_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
refresh_rate: Duration,
client_version: String,
}
impl TopologyRefresherConfig {
pub fn new(
validator_api_urls: Vec<Url>,
refresh_rate: Duration,
client_version: String,
) -> Self {
pub fn new(nym_api_urls: Vec<Url>, refresh_rate: Duration, client_version: String) -> Self {
TopologyRefresherConfig {
validator_api_urls,
nym_api_urls,
refresh_rate,
client_version,
}
@@ -159,7 +155,7 @@ pub struct TopologyRefresher {
validator_client: validator_client::client::ApiClient,
client_version: String,
validator_api_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
topology_accessor: TopologyAccessor,
refresh_rate: Duration,
@@ -169,14 +165,12 @@ pub struct TopologyRefresher {
impl TopologyRefresher {
pub fn new(mut cfg: TopologyRefresherConfig, topology_accessor: TopologyAccessor) -> Self {
cfg.validator_api_urls.shuffle(&mut thread_rng());
cfg.nym_api_urls.shuffle(&mut thread_rng());
TopologyRefresher {
validator_client: validator_client::client::ApiClient::new(
cfg.validator_api_urls[0].clone(),
),
validator_client: validator_client::client::ApiClient::new(cfg.nym_api_urls[0].clone()),
client_version: cfg.client_version,
validator_api_urls: cfg.validator_api_urls,
nym_api_urls: cfg.nym_api_urls,
topology_accessor,
refresh_rate: cfg.refresh_rate,
currently_used_api: 0,
@@ -184,15 +178,15 @@ impl TopologyRefresher {
}
}
fn use_next_validator_api(&mut self) {
if self.validator_api_urls.len() == 1 {
warn!("There's only a single validator API available - it won't be possible to use a different one");
fn use_next_nym_api(&mut self) {
if self.nym_api_urls.len() == 1 {
warn!("There's only a single nym API available - it won't be possible to use a different one");
return;
}
self.currently_used_api = (self.currently_used_api + 1) % self.validator_api_urls.len();
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
self.validator_client
.change_validator_api(self.validator_api_urls[self.currently_used_api].clone())
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
}
/// Verifies whether nodes a reasonably distributed among all mix layers.
@@ -288,7 +282,7 @@ impl TopologyRefresher {
let new_topology = self.get_current_compatible_topology().await;
if new_topology.is_none() {
self.use_next_validator_api();
self.use_next_nym_api();
}
if new_topology.is_none() && self.was_latest_valid {
@@ -310,7 +304,7 @@ impl TopologyRefresher {
self.topology_accessor.ensure_is_routable().await
}
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
pub fn start_with_shutdown(mut self, mut shutdown: task::TaskClient) {
spawn_future(async move {
debug!("Started TopologyRefresher with graceful shutdown support");
+19 -38
View File
@@ -88,28 +88,16 @@ impl<T> Config<T> {
where
T: NymConfig,
{
self.client.id = id.into();
self.set_empty_fields_to_defaults();
self
}
pub fn set_empty_fields_to_defaults(&mut self) -> bool
where
T: NymConfig,
{
let id = &self.client.id;
let mut changes_made = false;
let id = id.into();
// identity key setting
if self.client.private_identity_key_file.as_os_str().is_empty() {
changes_made = true;
self.client.private_identity_key_file =
self::Client::<T>::default_private_identity_key_file(id);
self::Client::<T>::default_private_identity_key_file(&id);
}
if self.client.public_identity_key_file.as_os_str().is_empty() {
changes_made = true;
self.client.public_identity_key_file =
self::Client::<T>::default_public_identity_key_file(id);
self::Client::<T>::default_public_identity_key_file(&id);
}
// encryption key setting
@@ -119,9 +107,8 @@ impl<T> Config<T> {
.as_os_str()
.is_empty()
{
changes_made = true;
self.client.private_encryption_key_file =
self::Client::<T>::default_private_encryption_key_file(id);
self::Client::<T>::default_private_encryption_key_file(&id);
}
if self
.client
@@ -129,35 +116,32 @@ impl<T> Config<T> {
.as_os_str()
.is_empty()
{
changes_made = true;
self.client.public_encryption_key_file =
self::Client::<T>::default_public_encryption_key_file(id);
self::Client::<T>::default_public_encryption_key_file(&id);
}
// shared gateway key setting
if self.client.gateway_shared_key_file.as_os_str().is_empty() {
changes_made = true;
self.client.gateway_shared_key_file =
self::Client::<T>::default_gateway_shared_key_file(id);
self::Client::<T>::default_gateway_shared_key_file(&id);
}
// ack key setting
if self.client.ack_key_file.as_os_str().is_empty() {
changes_made = true;
self.client.ack_key_file = self::Client::<T>::default_ack_key_file(id);
self.client.ack_key_file = self::Client::<T>::default_ack_key_file(&id);
}
if self.client.reply_surb_database_path.as_os_str().is_empty() {
changes_made = true;
self.client.reply_surb_database_path =
self::Client::<T>::default_reply_surb_database_path(id);
self::Client::<T>::default_reply_surb_database_path(&id);
}
if self.client.database_path.as_os_str().is_empty() {
changes_made = true;
self.client.database_path = self::Client::<T>::default_database_path(id);
self.client.database_path = self::Client::<T>::default_database_path(&id);
}
changes_made
self.client.id = id;
self
}
pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) {
@@ -176,8 +160,8 @@ impl<T> Config<T> {
self.client.validator_urls = validator_urls;
}
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
self.client.validator_api_urls = validator_api_urls;
pub fn set_custom_nym_apis(&mut self, nym_api_urls: Vec<Url>) {
self.client.nym_api_urls = nym_api_urls;
}
pub fn set_high_default_traffic_volume(&mut self) {
@@ -237,8 +221,8 @@ impl<T> Config<T> {
self.client.validator_urls.clone()
}
pub fn get_validator_api_endpoints(&self) -> Vec<Url> {
self.client.validator_api_urls.clone()
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.client.nym_api_urls.clone()
}
pub fn get_gateway_id(&self) -> String {
@@ -425,7 +409,8 @@ pub struct Client<T> {
validator_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls: Vec<Url>,
#[serde(alias = "validator_api_urls")]
nym_api_urls: Vec<Url>,
/// Path to file containing private identity key.
private_identity_key_file: PathBuf,
@@ -454,10 +439,6 @@ pub struct Client<T> {
database_path: PathBuf,
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
// this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4.
// if you're reading this message and we have already introduced some breaking changes, feel free
// to remove that attribute since at this point the client configs should have gotten regenerated
#[serde(default)]
reply_surb_database_path: PathBuf,
/// nym_home_directory specifies absolute path to the home nym Clients directory.
@@ -476,7 +457,7 @@ impl<T: NymConfig> Default for Client<T> {
id: "".to_string(),
disabled_credentials_mode: true,
validator_urls: vec![],
validator_api_urls: vec![],
nym_api_urls: vec![],
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_encryption_key_file: Default::default(),
+2 -2
View File
@@ -30,8 +30,8 @@ pub enum ClientCoreError<B: ReplyStorageBackend> {
#[error("Failed to setup gateway")]
FailedToSetupGateway,
#[error("List of validator apis is empty")]
ListOfValidatorApisIsEmpty,
#[error("List of nym apis is empty")]
ListOfNymApisIsEmpty,
#[error("Could not load existing gateway configuration: {0}")]
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
+4 -4
View File
@@ -24,12 +24,12 @@ pub(super) async fn query_gateway_details<B>(
where
B: ReplyStorageBackend,
{
let validator_api = validator_servers
let nym_api = validator_servers
.choose(&mut thread_rng())
.ok_or(ClientCoreError::ListOfValidatorApisIsEmpty)?;
let validator_client = validator_client::client::ApiClient::new(validator_api.clone());
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let validator_client = validator_client::client::ApiClient::new(nym_api.clone());
log::trace!("Fetching list of gateways from: {}", validator_api);
log::trace!("Fetching list of gateways from: {}", nym_api);
let gateways = validator_client.get_cached_gateways().await?;
let valid_gateways = gateways
.into_iter()
+5 -8
View File
@@ -96,7 +96,7 @@ where
{
println!("Configuring gateway");
let gateway =
query_gateway_details(config.get_validator_api_endpoints(), user_chosen_gateway_id).await?;
query_gateway_details(config.get_nym_api_endpoints(), user_chosen_gateway_id).await?;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
@@ -120,11 +120,8 @@ where
T: NymConfig,
{
println!("Using gateway provided by user, keeping existing keys");
let gateway = query_gateway_details(
config.get_validator_api_endpoints(),
Some(user_chosen_gateway_id),
)
.await?;
let gateway =
query_gateway_details(config.get_nym_api_endpoints(), Some(user_chosen_gateway_id)).await?;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
}
@@ -209,8 +206,8 @@ pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
match std::fs::File::create(output_file) {
Ok(file) => match serde_json::to_writer_pretty(file, init_results) {
Ok(_) => println!("Saved: {}", output_file),
Err(err) => eprintln!("Could not save {}: {}", output_file, err),
Err(err) => eprintln!("Could not save {}: {err}", output_file),
},
Err(err) => eprintln!("Could not save {}: {}", output_file, err),
Err(err) => eprintln!("Could not save {}: {err}", output_file),
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.4"
version = "1.1.3"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+2 -2
View File
@@ -31,8 +31,8 @@ validator_urls = [
]
# Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls = [
{{#each client.validator_api_urls }}
nym_api_urls = [
{{#each client.nym_api_urls }}
'{{this}}',
{{/each}}
]
+24 -19
View File
@@ -1,6 +1,8 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use crate::client::config::Config;
use crate::error::ClientError;
use crate::websocket;
@@ -18,7 +20,7 @@ use log::*;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
use nymsphinx::receiver::ReconstructedMessage;
use task::{wait_for_signal, ShutdownNotifier};
use task::TaskManager;
pub(crate) mod config;
@@ -56,7 +58,7 @@ impl SocketClient {
.expect("No nymd validator endpoint provided");
let api_url = config
.get_base()
.get_validator_api_endpoints()
.get_nym_api_endpoints()
.pop()
.expect("No validator api endpoint provided");
// overwrite env configuration with config URLs
@@ -85,16 +87,19 @@ impl SocketClient {
client_input: ClientInput,
client_output: ClientOutput,
self_address: Recipient,
shutdown: task::TaskClient,
) {
info!("Starting websocket listener...");
let ClientInput {
shared_lane_queue_lengths,
connection_command_sender,
input_sender,
} = client_input;
let received_buffer_request_sender = client_output.received_buffer_request_sender;
let ClientOutput {
shared_lane_queue_lengths,
received_buffer_request_sender,
} = client_output;
let websocket_handler = websocket::Handler::new(
input_sender,
@@ -104,32 +109,26 @@ impl SocketClient {
shared_lane_queue_lengths,
);
websocket::Listener::new(config.get_listening_port()).start(websocket_handler);
websocket::Listener::new(config.get_listening_port()).start(websocket_handler, shutdown);
}
/// blocking version of `start_socket` method. Will run forever (or until SIGINT is sent)
pub async fn run_socket_forever(self) -> Result<(), ClientError> {
pub async fn run_socket_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut shutdown = self.start_socket().await?;
wait_for_signal().await;
println!(
"Received signal - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
);
let res = task::wait_for_signal_and_error(&mut shutdown).await;
log::info!("Sending shutdown");
shutdown.signal_shutdown().ok();
// Some of these components have shutdown signalling implemented as part of socks5 work,
// but since it's not fully implemented (yet) for all the components of the native client,
// we don't try to wait and instead just stop immediately.
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-client");
Ok(())
res
}
pub async fn start_socket(self) -> Result<ShutdownNotifier, ClientError> {
pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
if !self.config.get_socket_type().is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
@@ -150,12 +149,18 @@ impl SocketClient {
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
Self::start_websocket_listener(&self.config, client_input, client_output, self_address);
Self::start_websocket_listener(
&self.config,
client_input,
client_output,
self_address,
started_client.task_manager.subscribe(),
);
info!("Client startup finished!");
info!("The address of this client is: {}", self_address);
Ok(started_client.shutdown_notifier)
Ok(started_client.task_manager)
}
pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
@@ -192,7 +197,7 @@ impl SocketClient {
Ok(DirectClient {
client_input,
reconstructed_receiver,
_shutdown_notifier: started_client.shutdown_notifier,
_shutdown_notifier: started_client.task_manager,
})
}
}
@@ -202,7 +207,7 @@ pub struct DirectClient {
reconstructed_receiver: ReconstructedMessagesReceiver,
// we need to keep reference to this guy otherwise things will start dropping
_shutdown_notifier: ShutdownNotifier,
_shutdown_notifier: TaskManager,
}
impl DirectClient {
+5 -4
View File
@@ -1,8 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use crate::client::config::{Config, SocketType};
use crate::error::ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use completions::{fig_generate, ArgShell};
@@ -86,7 +87,7 @@ pub(crate) struct OverrideConfig {
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) -> Result<(), ClientError> {
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
let bin_name = "nym-native-client";
match &args.command {
@@ -114,13 +115,13 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
if let Some(raw_validators) = args.api_validators {
config
.get_base_mut()
.set_custom_validator_apis(config::parse_validators(&raw_validators));
.set_custom_nym_apis(config::parse_validators(&raw_validators));
} else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() {
let raw_validators = std::env::var(network_defaults::var_names::API_VALIDATOR)
.expect("api validator not set");
config
.get_base_mut()
.set_custom_validator_apis(config::parse_validators(&raw_validators));
.set_custom_nym_apis(config::parse_validators(&raw_validators));
}
if args.disable_socket {
+6 -8
View File
@@ -1,6 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use crate::{
client::{config::Config, SocketClient},
commands::{override_config, OverrideConfig},
@@ -89,27 +91,23 @@ fn version_check(cfg: &Config) -> bool {
}
}
pub(crate) async fn execute(args: &Run) -> Result<(), ClientError> {
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
let id = &args.id;
let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
return Err(ClientError::FailedToLoadConfig(id.to_string()));
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
return Err(Box::new(ClientError::FailedToLoadConfig(id.to_string())));
}
};
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
if config.get_base_mut().set_empty_fields_to_defaults() {
warn!("some of the core config options were left unset. the default values are going to get used instead.");
}
if !version_check(&config) {
error!("failed the local version check");
return Err(ClientError::FailedLocalVersionCheck);
return Err(Box::new(ClientError::FailedLocalVersionCheck));
}
SocketClient::new(config).run_socket_forever().await
+3 -3
View File
@@ -59,7 +59,7 @@ pub(crate) struct Upgrade {
fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {:?}", err);
eprintln!("failed to parse client version! - {err}");
process::exit(1)
});
@@ -110,7 +110,7 @@ fn minor_0_12_upgrade(
.set_custom_version(to_version.to_string().as_ref());
config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
eprintln!("failed to overwrite config file! - {err}");
print_failed_upgrade(config_version, &to_version);
process::exit(1);
});
@@ -146,7 +146,7 @@ pub(crate) fn execute(args: &Upgrade) {
let id = &args.id;
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {:?}", err);
eprintln!("failed to load existing config file! - {err}");
process::exit(1)
});
+3 -2
View File
@@ -1,8 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use clap::{crate_version, Parser};
use error::ClientError;
use logging::setup_logging;
use network_defaults::setup_env;
@@ -12,7 +13,7 @@ pub mod error;
pub mod websocket;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
setup_logging();
println!("{}", banner());
+47 -27
View File
@@ -43,29 +43,16 @@ pub(crate) struct Handler {
socket: Option<WebSocketStream<TcpStream>>,
received_response_type: ReceivedResponseType,
lane_queue_lengths: LaneQueueLengths,
}
// clone is used to use handler on a new connection, which initially is `None`
impl Clone for Handler {
fn clone(&self) -> Self {
Handler {
msg_input: self.msg_input.clone(),
client_connection_tx: self.client_connection_tx.clone(),
buffer_requester: self.buffer_requester.clone(),
self_full_address: self.self_full_address,
socket: None,
received_response_type: Default::default(),
lane_queue_lengths: self.lane_queue_lengths.clone(),
}
}
is_active: bool,
}
impl Drop for Handler {
fn drop(&mut self) {
if self
.buffer_requester
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
.is_err()
if self.is_active
&& self
.buffer_requester
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
.is_err()
{
error!("we failed to disconnect the receiver from the buffer! presumably the shutdown procedure has been initiated!")
}
@@ -88,6 +75,22 @@ impl Handler {
socket: None,
received_response_type: Default::default(),
lane_queue_lengths,
is_active: false,
}
}
// Used to use handler on a new connection, which initially is `None`
// TODO: make sure we only ever have one active handler
pub fn create_active_handler(&self) -> Self {
Handler {
msg_input: self.msg_input.clone(),
client_connection_tx: self.client_connection_tx.clone(),
buffer_requester: self.buffer_requester.clone(),
self_full_address: self.self_full_address,
socket: None,
received_response_type: Default::default(),
lane_queue_lengths: self.lane_queue_lengths.clone(),
is_active: true,
}
}
@@ -362,8 +365,12 @@ impl Handler {
}
}
async fn listen_for_requests(&mut self, mut msg_receiver: ReconstructedMessagesReceiver) {
loop {
async fn listen_for_requests(
&mut self,
mut msg_receiver: ReconstructedMessagesReceiver,
mut task_client: task::TaskClient,
) {
while !task_client.is_shutdown() {
tokio::select! {
// we can either get a client request from the websocket
socket_msg = self.next_websocket_request() => {
@@ -373,7 +380,7 @@ impl Handler {
let socket_msg = match socket_msg.unwrap() {
Ok(socket_msg) => socket_msg,
Err(err) => {
warn!("failed to obtain message from websocket stream! stopping connection handler: {}", err);
warn!("failed to obtain message from websocket stream! stopping connection handler: {err}");
break;
}
};
@@ -397,21 +404,33 @@ impl Handler {
error!("mix messages sender was unexpectedly closed! this shouldn't have ever happened! (unless we're shutting down - TODO: implement proper graceful shutdown handler)");
return
};
if let Err(e) = self.push_websocket_received_plaintexts(mix_messages).await {
warn!("failed to send sphinx packets back to the client - {:?}, assuming the connection is dead", e);
if let Err(err) = self.push_websocket_received_plaintexts(mix_messages).await {
warn!("failed to send sphinx packets back to the client - {err}, assuming the connection is dead");
break;
}
}
_ = task_client.recv() => {
log::trace!("Websocket handler: Received shutdown");
}
}
}
log::debug!("Websocket handler: Exiting");
}
// consume self to make sure `drop` is called after this is done
pub(crate) async fn handle_connection(mut self, socket: TcpStream) {
pub(crate) async fn handle_connection(
mut self,
socket: TcpStream,
mut task_client: task::TaskClient,
) {
// We don't want a crash in the connection handler to trigger a shutdown of the whole
// process.
task_client.mark_as_success();
let ws_stream = match accept_async(socket).await {
Ok(ws_stream) => ws_stream,
Err(err) => {
warn!("error while performing the websocket handshake - {:?}", err);
warn!("error while performing the websocket handshake - {err}");
return;
}
};
@@ -426,7 +445,8 @@ impl Handler {
))
.expect("the buffer request failed!");
self.listen_for_requests(reconstructed_receiver).await;
self.listen_for_requests(reconstructed_receiver, task_client)
.await;
}
}
+23 -8
View File
@@ -32,11 +32,11 @@ impl Listener {
}
}
pub(crate) async fn run(&mut self, handler: Handler) {
pub(crate) async fn run(&mut self, handler: Handler, mut task_client: task::TaskClient) {
let tcp_listener = match tokio::net::TcpListener::bind(self.address).await {
Ok(listener) => listener,
Err(err) => {
error!("Failed to bind to {} - {}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?", self.address, err);
error!("Failed to bind to {} - {err}. Are you sure nothing else is running on the specified port and your user has sufficient permission to bind to the requested address?", self.address);
process::exit(1);
}
};
@@ -45,10 +45,23 @@ impl Listener {
loop {
tokio::select! {
// When the handler finishes we check if shutdown is signalled
_ = notify.notified() => {
if task_client.is_shutdown() {
log::trace!("Websocket listener: detected shutdown after connection closed");
break;
}
// our connection terminated - we are open to a new one now!
self.state = State::AwaitingConnection;
}
// ... but when there is no connected client at the time of shutdown being
// signalled, we handle it here.
_ = task_client.recv() => {
if !self.state.is_connected() {
log::trace!("Not connected: shutting down");
break;
}
}
new_conn = tcp_listener.accept() => {
match new_conn {
Ok((mut socket, remote_addr)) => {
@@ -63,31 +76,33 @@ impl Listener {
Ok(_) => trace!(
"closed the connection between attempting websocket handshake"
),
Err(e) => warn!("failed to cleanly close the connection - {:?}", e),
Err(err) => warn!("failed to cleanly close the connection - {err}"),
};
} else {
// even though we're spawning a new task with the handler here, we will only ever spawn a single one.
// it's done so that any new connections to this listener could be rejected rather than left
// hanging because the executor doesn't come back here
let notify_clone = Arc::clone(&notify);
let fresh_handler = handler.clone();
let fresh_handler = handler.create_active_handler();
let task_client_handler = task_client.clone();
tokio::spawn(async move {
fresh_handler.handle_connection(socket).await;
fresh_handler.handle_connection(socket, task_client_handler).await;
notify_clone.notify_one();
});
self.state = State::Connected;
}
}
Err(e) => warn!("failed to get client: {:?}", e),
Err(err) => warn!("failed to get client: {err}"),
}
}
}
}
log::debug!("Websocket listener: Exiting");
}
pub(crate) fn start(mut self, handler: Handler) -> JoinHandle<()> {
pub(crate) fn start(mut self, handler: Handler, shutdown: task::TaskClient) -> JoinHandle<()> {
info!("Running websocket on {:?}", self.address.to_string());
tokio::spawn(async move { self.run(handler).await })
tokio::spawn(async move { self.run(handler, shutdown).await })
}
}
@@ -132,7 +132,7 @@ impl ClientRequest {
Err(err) => {
return Err(error::Error::new(
ErrorKind::MalformedRequest,
format!("malformed recipient: {:?}", err),
format!("malformed recipient: {err}"),
))
}
};
@@ -210,7 +210,7 @@ impl ClientRequest {
Err(err) => {
return Err(error::Error::new(
ErrorKind::MalformedRequest,
format!("malformed recipient: {:?}", err),
format!("malformed recipient: {err}"),
))
}
};
@@ -173,7 +173,7 @@ impl ServerResponse {
Err(err) => {
return Err(error::Error::new(
ErrorKind::MalformedResponse,
format!("malformed Recipient: {:?}", err),
format!("malformed Recipient: {err}"),
))
}
};
@@ -249,7 +249,7 @@ impl ServerResponse {
Err(err) => {
return Err(error::Error::new(
ErrorKind::MalformedResponse,
format!("malformed error message: {:?}", err),
format!("malformed error message: {err}"),
))
}
};
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.4"
version = "1.1.3"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
-1
View File
@@ -143,7 +143,6 @@ pub struct Socks5 {
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
///
/// Note that some service providers might not support this.
#[serde(default)]
send_anonymously: bool,
}
+2 -2
View File
@@ -31,8 +31,8 @@ validator_urls = [
]
# Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls = [
{{#each client.validator_api_urls }}
nym_api_urls = [
{{#each client.nym_api_urls }}
'{{this}}',
{{/each}}
]
+10 -8
View File
@@ -19,7 +19,7 @@ use gateway_client::bandwidth::BandwidthController;
use log::*;
use nymsphinx::addressing::clients::Recipient;
use std::error::Error;
use task::{wait_for_signal_and_error, ShutdownListener, ShutdownNotifier};
use task::{wait_for_signal_and_error, TaskClient, TaskManager};
pub mod config;
@@ -67,7 +67,7 @@ impl NymClient {
.expect("No nymd validator endpoint provided");
let api_url = config
.get_base()
.get_validator_api_endpoints()
.get_nym_api_endpoints()
.pop()
.expect("No validator api endpoint provided");
// overwrite env configuration with config URLs
@@ -96,19 +96,21 @@ impl NymClient {
client_input: ClientInput,
client_output: ClientOutput,
self_address: Recipient,
shutdown: ShutdownListener,
shutdown: TaskClient,
) {
info!("Starting socks5 listener...");
let auth_methods = vec![AuthenticationMethods::NoAuth as u8];
let allowed_users: Vec<User> = Vec::new();
let ClientInput {
shared_lane_queue_lengths,
connection_command_sender,
input_sender,
} = client_input;
let received_buffer_request_sender = client_output.received_buffer_request_sender;
let ClientOutput {
shared_lane_queue_lengths,
received_buffer_request_sender,
} = client_output;
let authenticator = Authenticator::new(auth_methods, allowed_users);
let mut sphinx_socks = SphinxSocksServer::new(
@@ -200,7 +202,7 @@ impl NymClient {
res
}
pub async fn start(self) -> Result<ShutdownNotifier, Socks5ClientError> {
pub async fn start(self) -> Result<TaskManager, Socks5ClientError> {
let base_builder = BaseClientBuilder::new_from_base_config(
self.config.get_base(),
self.key_manager,
@@ -222,12 +224,12 @@ impl NymClient {
client_input,
client_output,
self_address,
started_client.shutdown_notifier.subscribe(),
started_client.task_manager.subscribe(),
);
info!("Client startup finished!");
info!("The address of this client is: {}", self_address);
Ok(started_client.shutdown_notifier)
Ok(started_client.task_manager)
}
}
+2 -2
View File
@@ -116,11 +116,11 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
if let Some(raw_validators) = args.api_validators {
config
.get_base_mut()
.set_custom_validator_apis(parse_validators(&raw_validators));
.set_custom_nym_apis(parse_validators(&raw_validators));
} else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::API_VALIDATOR) {
config
.get_base_mut()
.set_custom_validator_apis(parse_validators(&raw_validators));
.set_custom_nym_apis(parse_validators(&raw_validators));
}
if args.use_anonymous_sender_tag {
+4 -8
View File
@@ -43,9 +43,9 @@ pub(crate) struct Run {
#[clap(long)]
nymd_validators: Option<String>,
/// Comma separated list of rest endpoints of the API validators
/// Comma separated list of rest endpoints of the Nym APIs
#[clap(long)]
api_validators: Option<String>,
nym_apis: Option<String>,
/// Port for the socket to listen on
#[clap(short, long)]
@@ -71,7 +71,7 @@ impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
nymd_validators: run_config.nymd_validators,
api_validators: run_config.api_validators,
api_validators: run_config.nym_apis,
port: run_config.port,
use_anonymous_sender_tag: run_config.use_anonymous_sender_tag,
fastmode: run_config.fastmode,
@@ -110,7 +110,7 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error
let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})", id, err);
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
return Err(Box::new(Socks5ClientError::FailedToLoadConfig(
id.to_string(),
)));
@@ -120,10 +120,6 @@ pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
if config.get_base_mut().set_empty_fields_to_defaults() {
warn!("some of the core config options were left unset. the default values are going to get used instead.");
}
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
+3 -3
View File
@@ -58,7 +58,7 @@ pub(crate) struct Upgrade {
fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {:?}", err);
eprintln!("failed to parse client version! - {err}");
process::exit(1)
});
@@ -109,7 +109,7 @@ fn minor_0_12_upgrade(
.set_custom_version(to_version.to_string().as_ref());
config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
eprintln!("failed to overwrite config file! - {err}");
print_failed_upgrade(config_version, &to_version);
process::exit(1);
});
@@ -145,7 +145,7 @@ pub(crate) fn execute(args: &Upgrade) {
let id = &args.id;
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {:?}", err);
eprintln!("failed to load existing config file! - {err}");
process::exit(1)
});
+4 -4
View File
@@ -20,7 +20,7 @@ use socks5_requests::{ConnectionId, Message, RemoteAddress, Request};
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
use task::ShutdownListener;
use task::TaskClient;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
use tokio::{self, net::TcpStream};
@@ -164,7 +164,7 @@ pub(crate) struct SocksClient {
self_address: Recipient,
started_proxy: bool,
lane_queue_lengths: LaneQueueLengths,
shutdown_listener: ShutdownListener,
shutdown_listener: TaskClient,
}
impl Drop for SocksClient {
@@ -190,7 +190,7 @@ impl SocksClient {
controller_sender: ControllerSender,
self_address: &Recipient,
lane_queue_lengths: LaneQueueLengths,
mut shutdown_listener: ShutdownListener,
mut shutdown_listener: TaskClient,
) -> Self {
// If this task fails and exits, we don't want to send shutdown signal
shutdown_listener.mark_as_success();
@@ -220,7 +220,7 @@ impl SocksClient {
}
pub async fn send_error(&mut self, err: SocksProxyError) -> Result<(), SocksProxyError> {
let error_text = format!("{}", err);
let error_text = format!("{err}");
let Some(ref version) = self.socks_version else {
log::error!("Trying to send error without knowing the version");
return Ok(());
+6 -6
View File
@@ -9,13 +9,13 @@ use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBuffer
use nymsphinx::receiver::ReconstructedMessage;
use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
use socks5_requests::Message;
use task::ShutdownListener;
use task::TaskClient;
pub(crate) struct MixnetResponseListener {
buffer_requester: ReceivedBufferRequestSender,
mix_response_receiver: ReconstructedMessagesReceiver,
controller_sender: ControllerSender,
shutdown: ShutdownListener,
shutdown: TaskClient,
}
impl Drop for MixnetResponseListener {
@@ -25,9 +25,9 @@ impl Drop for MixnetResponseListener {
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
{
if self.shutdown.is_shutdown_poll() {
log::debug!("The buffer request failed: {}", err);
log::debug!("The buffer request failed: {err}");
} else {
log::error!("The buffer request failed: {}", err);
log::error!("The buffer request failed: {err}");
}
}
}
@@ -37,7 +37,7 @@ impl MixnetResponseListener {
pub(crate) fn new(
buffer_requester: ReceivedBufferRequestSender,
controller_sender: ControllerSender,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> Self {
let (mix_response_sender, mix_response_receiver) = mpsc::unbounded();
buffer_requester
@@ -60,7 +60,7 @@ impl MixnetResponseListener {
let response = match Message::try_from_bytes(&raw_message) {
Err(err) => {
warn!("failed to parse received response - {:?}", err);
warn!("failed to parse received response - {err}");
return;
}
Ok(Message::Request(_)) => {
+4 -4
View File
@@ -13,7 +13,7 @@ use nymsphinx::addressing::clients::Recipient;
use proxy_helpers::connection_controller::{BroadcastActiveConnections, Controller};
use std::net::SocketAddr;
use tap::TapFallible;
use task::ShutdownListener;
use task::TaskClient;
use tokio::net::TcpListener;
/// A Socks5 server that listens for connections.
@@ -24,7 +24,7 @@ pub struct SphinxSocksServer {
self_address: Recipient,
client_config: client::Config,
lane_queue_lengths: LaneQueueLengths,
shutdown: ShutdownListener,
shutdown: TaskClient,
}
impl SphinxSocksServer {
@@ -36,7 +36,7 @@ impl SphinxSocksServer {
self_address: Recipient,
lane_queue_lengths: LaneQueueLengths,
client_config: client::Config,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> Self {
// hardcode ip as we (presumably) ONLY want to listen locally. If we change it, we can
// just modify the config
@@ -103,7 +103,7 @@ impl SphinxSocksServer {
tokio::spawn(async move {
if let Err(err) = client.run().await {
error!("Error! {}", err);
error!("Error! {err}");
if client.send_error(err).await.is_err() {
warn!("Failed to send error code");
};
+1 -1
View File
@@ -40,7 +40,7 @@ pub enum SocksProxyError {
impl std::fmt::Display for SocksProxyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SocksProxyError::GenericError(err) => write!(f, "GenericError - {}", err),
SocksProxyError::GenericError(err) => write!(f, "GenericError - {err}"),
SocksProxyError::UnsupportedProxyVersion(version) => {
write!(f, "Unsupported proxy version {}", version)
}
+4 -4
View File
@@ -86,7 +86,7 @@ export default class ValidatorClient implements INymClient {
static async connect(
mnemonic: string,
nymdUrl: string,
validatorApiUrl: string,
nymApiUrl: string,
prefix: string,
mixnetContract: string,
vestingContract: string,
@@ -94,19 +94,19 @@ export default class ValidatorClient implements INymClient {
): Promise<ValidatorClient> {
const wallet = await ValidatorClient.buildWallet(mnemonic, prefix);
const signingClient = await SigningClient.connectWithNymSigner(wallet, nymdUrl, validatorApiUrl, prefix, denom);
const signingClient = await SigningClient.connectWithNymSigner(wallet, nymdUrl, nymApiUrl, prefix, denom);
return new ValidatorClient(signingClient, prefix, mixnetContract, vestingContract, denom);
}
static async connectForQuery(
nymdUrl: string,
validatorApiUrl: string,
nymApiUrl: string,
prefix: string,
mixnetContract: string,
vestingContract: string,
denom: string,
): Promise<ValidatorClient> {
const queryClient = await QueryClient.connectWithNym(nymdUrl, validatorApiUrl);
const queryClient = await QueryClient.connectWithNym(nymdUrl, nymApiUrl);
return new ValidatorClient(queryClient, prefix, mixnetContract, vestingContract, denom);
}
@@ -6,13 +6,13 @@
import axios from 'axios';
import { GatewayBond, MixNodeBond } from './types';
export const VALIDATOR_API_VERSION = '/v1';
export const VALIDATOR_API_GATEWAYS_PATH = `${VALIDATOR_API_VERSION}/gateways`;
export const VALIDATOR_API_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes`;
export const VALIDATOR_API_ACTIVE_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes/active`;
export const VALIDATOR_API_REWARDED_MIXNODES_PATH = `${VALIDATOR_API_VERSION}/mixnodes/rewarded`;
export const NYM_API_VERSION = '/v1';
export const NYM_API_GATEWAYS_PATH = `${NYM_API_VERSION}/gateways`;
export const NYM_API_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes`;
export const NYM_API_ACTIVE_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes/active`;
export const NYM_API_REWARDED_MIXNODES_PATH = `${NYM_API_VERSION}/mixnodes/rewarded`;
export interface IValidatorApiQuery {
export interface INymApiQuery {
getCachedMixnodes(): Promise<MixNodeBond[]>;
getCachedGateways(): Promise<GatewayBond[]>;
@@ -22,16 +22,16 @@ export interface IValidatorApiQuery {
getRewardedMixnodes(): Promise<MixNodeBond[]>;
}
export default class ValidatorApiQuerier implements IValidatorApiQuery {
validatorApiUrl: string;
export default class NymApiQuerier implements INymApiQuery {
nymApiUrl: string;
constructor(validatorApiUrl: string) {
this.validatorApiUrl = validatorApiUrl;
constructor(nymApiUrl: string) {
this.nymApiUrl = nymApiUrl;
}
async getCachedMixnodes(): Promise<MixNodeBond[]> {
const url = new URL(this.validatorApiUrl);
url.pathname += VALIDATOR_API_MIXNODES_PATH;
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_MIXNODES_PATH;
const response = await axios.get(url.toString());
if (response.status === 200) {
@@ -41,8 +41,8 @@ export default class ValidatorApiQuerier implements IValidatorApiQuery {
}
async getCachedGateways(): Promise<GatewayBond[]> {
const url = new URL(this.validatorApiUrl);
url.pathname += VALIDATOR_API_GATEWAYS_PATH;
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_GATEWAYS_PATH;
const response = await axios.get(url.toString());
if (response.status === 200) {
@@ -52,8 +52,8 @@ export default class ValidatorApiQuerier implements IValidatorApiQuery {
}
async getActiveMixnodes(): Promise<MixNodeBond[]> {
const url = new URL(this.validatorApiUrl);
url.pathname += VALIDATOR_API_ACTIVE_MIXNODES_PATH;
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_ACTIVE_MIXNODES_PATH;
const response = await axios.get(url.toString());
if (response.status === 200) {
@@ -63,8 +63,8 @@ export default class ValidatorApiQuerier implements IValidatorApiQuery {
}
async getRewardedMixnodes(): Promise<MixNodeBond[]> {
const url = new URL(this.validatorApiUrl);
url.pathname += VALIDATOR_API_REWARDED_MIXNODES_PATH;
const url = new URL(this.nymApiUrl);
url.pathname += NYM_API_REWARDED_MIXNODES_PATH;
const response = await axios.get(url.toString());
if (response.status === 200) {
+11 -11
View File
@@ -29,7 +29,7 @@ import {
PagedMixnodeResponse,
RewardingStatus,
} from './types';
import ValidatorApiQuerier, { IValidatorApiQuery } from './validator-api-querier';
import NymApiQuerier, { INymApiQuery as INymApiQuery } from './nym-api-querier';
export interface ICosmWasmQuery {
// methods exposed by `CosmWasmClient`
@@ -93,22 +93,22 @@ export interface INymdQuery {
): Promise<RewardingStatus>;
}
export interface IQueryClient extends ICosmWasmQuery, INymdQuery, IValidatorApiQuery {}
export interface IQueryClient extends ICosmWasmQuery, INymdQuery, INymApiQuery { }
export default class QueryClient extends CosmWasmClient implements IQueryClient {
private nymdQuerier: NymdQuerier;
private validatorApiQuerier: ValidatorApiQuerier;
private nymApiQuerier: NymApiQuerier;
private constructor(tmClient: Tendermint34Client, validatorApiUrl: string) {
private constructor(tmClient: Tendermint34Client, nymApiUrl: string) {
super(tmClient);
this.nymdQuerier = new NymdQuerier(this);
this.validatorApiQuerier = new ValidatorApiQuerier(validatorApiUrl);
this.nymApiQuerier = new NymApiQuerier(nymApiUrl);
}
public static async connectWithNym(nymdUrl: string, validatorApiUrl: string): Promise<QueryClient> {
public static async connectWithNym(nymdUrl: string, nymApiUrl: string): Promise<QueryClient> {
const tmClient = await Tendermint34Client.connect(nymdUrl);
return new QueryClient(tmClient, validatorApiUrl);
return new QueryClient(tmClient, nymApiUrl);
}
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
@@ -194,18 +194,18 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
}
getCachedGateways(): Promise<GatewayBond[]> {
return this.validatorApiQuerier.getCachedGateways();
return this.nymApiQuerier.getCachedGateways();
}
getCachedMixnodes(): Promise<MixNodeBond[]> {
return this.validatorApiQuerier.getCachedMixnodes();
return this.nymApiQuerier.getCachedMixnodes();
}
getActiveMixnodes(): Promise<MixNodeBond[]> {
return this.validatorApiQuerier.getActiveMixnodes();
return this.nymApiQuerier.getActiveMixnodes();
}
getRewardedMixnodes(): Promise<MixNodeBond[]> {
return this.validatorApiQuerier.getRewardedMixnodes();
return this.nymApiQuerier.getRewardedMixnodes();
}
}
+10 -10
View File
@@ -33,7 +33,7 @@ import {
PagedMixnodeResponse,
RewardingStatus,
} from './types';
import ValidatorApiQuerier from './validator-api-querier';
import NymApiQuerier from './nym-api-querier';
// methods exposed by `SigningCosmWasmClient`
export interface ICosmWasmSigning {
@@ -199,13 +199,13 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
export default class SigningClient extends SigningCosmWasmClient implements ISigningClient {
private nymdQuerier: NymdQuerier;
private validatorApiQuerier: ValidatorApiQuerier;
private nymApiQuerier: NymApiQuerier;
clientAddress: string;
private constructor(
clientAddress: string,
validatorApiUrl: string,
nymApiUrl: string,
tmClient: Tendermint34Client,
wallet: DirectSecp256k1HdWallet,
signerOptions: SigningCosmWasmClientOptions,
@@ -213,13 +213,13 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
super(tmClient, wallet, signerOptions);
this.clientAddress = clientAddress;
this.nymdQuerier = new NymdQuerier(this);
this.validatorApiQuerier = new ValidatorApiQuerier(validatorApiUrl);
this.nymApiQuerier = new NymApiQuerier(nymApiUrl);
}
public static async connectWithNymSigner(
wallet: DirectSecp256k1HdWallet,
nymdUrl: string,
validatorApiUrl: string,
nymApiUrl: string,
prefix: string,
denom: string,
): Promise<SigningClient> {
@@ -229,7 +229,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
gasPrice: nymGasPrice(denom),
};
const tmClient = await Tendermint34Client.connect(nymdUrl);
return new SigningClient(address, validatorApiUrl, tmClient, wallet, signerOptions);
return new SigningClient(address, nymApiUrl, tmClient, wallet, signerOptions);
}
// query related:
@@ -317,19 +317,19 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
}
getCachedGateways(): Promise<GatewayBond[]> {
return this.validatorApiQuerier.getCachedGateways();
return this.nymApiQuerier.getCachedGateways();
}
getCachedMixnodes(): Promise<MixNodeBond[]> {
return this.validatorApiQuerier.getCachedMixnodes();
return this.nymApiQuerier.getCachedMixnodes();
}
getActiveMixnodes(): Promise<MixNodeBond[]> {
return this.validatorApiQuerier.getActiveMixnodes();
return this.nymApiQuerier.getActiveMixnodes();
}
getRewardedMixnodes(): Promise<MixNodeBond[]> {
return this.validatorApiQuerier.getRewardedMixnodes();
return this.nymApiQuerier.getRewardedMixnodes();
}
// signing related:
@@ -1,4 +1,4 @@
import ValidatorClient from '../../dist';
import ValidatorClient from '../../validator/index';
import expect from 'expect';
describe('Query: balances', () => {
+2 -2
View File
@@ -17,7 +17,7 @@ pub struct Config {
/// ID specifies the human readable ID of this particular client.
pub(crate) id: String,
pub(crate) validator_api_url: Url,
pub(crate) nym_api_url: Url,
pub(crate) disabled_credentials_mode: bool,
@@ -38,7 +38,7 @@ impl Config {
) -> Self {
Config {
id,
validator_api_url: validator_server
nym_api_url: validator_server
.parse()
.expect("provided url was malformed"),
disabled_credentials_mode: true,
+4 -4
View File
@@ -14,7 +14,7 @@ use nymsphinx::addressing::clients::Recipient;
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
use rand::rngs::OsRng;
use std::sync::Arc;
use task::ShutdownNotifier;
use task::TaskManager;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{console_error, console_log};
@@ -30,7 +30,7 @@ pub struct NymClient {
// even though we don't use graceful shutdowns, other components rely on existence of this struct
// and if it's dropped, everything will start going offline
_shutdown: ShutdownNotifier,
_task_manager: TaskManager,
}
#[wasm_bindgen]
@@ -99,7 +99,7 @@ impl NymClientBuilder {
self.bandwidth_controller,
self.reply_surb_storage_backend,
self.disabled_credentials,
vec![self.config.validator_api_url.clone()],
vec![self.config.nym_api_url.clone()],
);
let self_address = base_builder.as_mix_recipient().to_string();
@@ -121,7 +121,7 @@ impl NymClientBuilder {
Ok(JsValue::from(NymClient {
self_address,
client_input: Arc::new(client_input),
_shutdown: started_client.shutdown_notifier,
_task_manager: started_client.task_manager,
}))
})
}
@@ -22,7 +22,7 @@ use rand::rngs::OsRng;
use std::convert::TryFrom;
use std::sync::Arc;
use std::time::Duration;
use task::ShutdownListener;
use task::TaskClient;
use tungstenite::protocol::Message;
#[cfg(feature = "coconut")]
@@ -67,7 +67,7 @@ pub struct GatewayClient {
reconnection_backoff: Duration,
/// Listen to shutdown messages.
shutdown: ShutdownListener,
shutdown: TaskClient,
}
impl GatewayClient {
@@ -83,7 +83,7 @@ impl GatewayClient {
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<PersistentStorage>>,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> Self {
GatewayClient {
authenticated: false,
@@ -135,7 +135,7 @@ impl GatewayClient {
// perfectly fine here, because it's not meant to be used
let (ack_tx, _) = mpsc::unbounded();
let (mix_tx, _) = mpsc::unbounded();
let shutdown = ShutdownListener::dummy();
let shutdown = TaskClient::dummy();
let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone());
GatewayClient {
@@ -305,11 +305,11 @@ impl GatewayClient {
if let Some(shared_keys) = &self.shared_key {
if let Some(plaintext) = try_decrypt_binary_message(bin_msg, shared_keys) {
if let Err(err) = self.packet_router.route_received(vec![plaintext]) {
log::warn!("Route received failed: {:?}", err);
log::warn!("Route received failed: {err}");
}
}
} else if let Err(err) = self.packet_router.route_received(vec![bin_msg]) {
log::warn!("Route received failed: {:?}", err);
log::warn!("Route received failed: {err}");
}
}
Message::Text(txt_msg) => {
@@ -9,7 +9,7 @@ use futures::channel::mpsc;
use log::*;
use nymsphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nymsphinx::params::packet_sizes::PacketSize;
use task::ShutdownListener;
use task::TaskClient;
pub type MixnetMessageSender = mpsc::UnboundedSender<Vec<Vec<u8>>>;
pub type MixnetMessageReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>;
@@ -21,14 +21,14 @@ pub type AcknowledgementReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>;
pub struct PacketRouter {
ack_sender: AcknowledgementSender,
mixnet_message_sender: MixnetMessageSender,
shutdown: ShutdownListener,
shutdown: TaskClient,
}
impl PacketRouter {
pub fn new(
ack_sender: AcknowledgementSender,
mixnet_message_sender: MixnetMessageSender,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> Self {
PacketRouter {
ack_sender,
@@ -89,14 +89,14 @@ impl PacketRouter {
}
// This should never happen during ordinary operation the way it's currently used.
// Abort to be on the safe side
panic!("Failed to send mixnet message: {:?}", err);
panic!("Failed to send mixnet message: {err}");
}
}
if !received_acks.is_empty() {
trace!("routing acks");
if let Err(e) = self.ack_sender.unbounded_send(received_acks) {
error!("failed to send ack: {:?}", e);
if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
error!("failed to send ack: {err}");
};
}
Ok(())
@@ -10,7 +10,7 @@ use futures::{SinkExt, StreamExt};
use gateway_requests::registration::handshake::SharedKeys;
use log::*;
use std::sync::Arc;
use task::ShutdownListener;
use task::TaskClient;
use tungstenite::Message;
#[cfg(not(target_arch = "wasm32"))]
@@ -84,7 +84,7 @@ impl PartiallyDelegated {
conn: WsConn,
packet_router: PacketRouter,
shared_key: Arc<SharedKeys>,
mut shutdown: ShutdownListener,
mut shutdown: TaskClient,
) -> Self {
// when called for, it NEEDS TO yield back the stream so that we could merge it and
// read control request responses.
@@ -100,7 +100,7 @@ impl PartiallyDelegated {
let ret_err = loop {
tokio::select! {
_ = shutdown.recv() => {
_ = shutdown.recv_with_delay() => {
log::trace!("GatewayClient listener: Received shutdown");
log::debug!("GatewayClient listener: Exiting");
return;
@@ -115,7 +115,7 @@ impl PartiallyDelegated {
};
if let Err(err) = Self::route_socket_messages(ws_msgs, &mut packet_router, shared_key.as_ref()) {
log::warn!("Route socket messages failed: {:?}", err);
log::warn!("Route socket messages failed: {err}");
}
}
};
@@ -122,7 +122,7 @@ impl Client {
// We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care
// about neither receiver nor the connection, it doesn't matter which one gets consumed
if let Err(err) = receiver.map(Ok).forward(conn).await {
warn!("Failed to forward packets to {} - {:?}", address, err);
warn!("Failed to forward packets to {} - {err}", address);
}
debug!(
@@ -59,7 +59,7 @@ impl PacketForwarder {
self.mixnet_client
.send_without_response(next_hop, sphinx_packet, packet_mode)
{
debug!("failed to forward the packet - {}", err)
debug!("failed to forward the packet - {err}")
}
}
}
@@ -29,7 +29,7 @@ futures = "0.3"
coconut-interface = { path = "../../coconut-interface" }
network-defaults = { path = "../../network-defaults" }
validator-api-requests = { path = "../../../validator-api/validator-api-requests", features = ["coconut"] }
nym-api-requests = { path = "../../../nym-api/nym-api-requests", features = ["coconut"] }
# required for nymd-client
# at some point it might be possible to make it wasm-compatible
@@ -1,16 +1,16 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{validator_api, ValidatorClientError};
use crate::{nym_api, ValidatorClientError};
use coconut_dkg_common::types::NodeIndex;
use coconut_interface::VerificationKey;
use mixnet_contract_common::mixnode::MixNodeDetails;
use mixnet_contract_common::MixId;
use mixnet_contract_common::{GatewayBond, IdentityKeyRef};
use validator_api_requests::coconut::{
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use validator_api_requests::models::{
use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
};
@@ -37,10 +37,10 @@ use mixnet_contract_common::{
#[cfg(feature = "nymd-client")]
use network_defaults::NymNetworkDetails;
#[cfg(feature = "nymd-client")]
use nym_api_requests::models::MixNodeBondAnnotated;
#[cfg(feature = "nymd-client")]
use std::str::FromStr;
use url::Url;
#[cfg(feature = "nymd-client")]
use validator_api_requests::models::MixNodeBondAnnotated;
#[cfg(feature = "nymd-client")]
#[must_use]
@@ -142,7 +142,7 @@ pub struct Client<C> {
proposals_page_limit: Option<u32>,
// ideally they would have been read-only, but unfortunately rust doesn't have such features
pub validator_api: validator_api::Client,
pub nym_api: nym_api::Client,
pub nymd: NymdClient<C>,
}
@@ -152,7 +152,7 @@ impl Client<SigningNymdClient> {
config: Config,
mnemonic: bip39::Mnemonic,
) -> Result<Client<SigningNymdClient>, ValidatorClientError> {
let validator_api_client = validator_api::Client::new(config.api_url.clone());
let nym_api_client = nym_api::Client::new(config.api_url.clone());
let nymd_client = NymdClient::connect_with_mnemonic(
config.nymd_config.clone(),
config.nymd_url.as_str(),
@@ -169,7 +169,7 @@ impl Client<SigningNymdClient> {
dealers_page_limit: config.dealers_page_limit,
verification_key_page_limit: config.verification_key_page_limit,
proposals_page_limit: config.proposals_page_limit,
validator_api: validator_api_client,
nym_api: nym_api_client,
nymd: nymd_client,
})
}
@@ -192,7 +192,7 @@ impl Client<SigningNymdClient> {
#[cfg(feature = "nymd-client")]
impl Client<QueryNymdClient> {
pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, ValidatorClientError> {
let validator_api_client = validator_api::Client::new(config.api_url.clone());
let nym_api_client = nym_api::Client::new(config.api_url.clone());
let nymd_client =
NymdClient::connect(config.nymd_config.clone(), config.nymd_url.as_str())?;
@@ -205,7 +205,7 @@ impl Client<QueryNymdClient> {
dealers_page_limit: config.dealers_page_limit,
verification_key_page_limit: config.verification_key_page_limit,
proposals_page_limit: config.proposals_page_limit,
validator_api: validator_api_client,
nym_api: nym_api_client,
nymd: nymd_client,
})
}
@@ -731,53 +731,53 @@ impl<C> Client<C> {
// validator-api wrappers
#[cfg(feature = "nymd-client")]
impl<C> Client<C> {
pub fn change_validator_api(&mut self, new_endpoint: Url) {
self.validator_api.change_url(new_endpoint)
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint)
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes().await?)
Ok(self.nym_api.get_mixnodes().await?)
}
pub async fn get_cached_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes_detailed().await?)
Ok(self.nym_api.get_mixnodes_detailed().await?)
}
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.validator_api.get_rewarded_mixnodes().await?)
Ok(self.nym_api.get_rewarded_mixnodes().await?)
}
pub async fn get_cached_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.validator_api.get_rewarded_mixnodes_detailed().await?)
Ok(self.nym_api.get_rewarded_mixnodes_detailed().await?)
}
pub async fn get_cached_active_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.validator_api.get_active_mixnodes().await?)
Ok(self.nym_api.get_active_mixnodes().await?)
}
pub async fn get_cached_active_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.validator_api.get_active_mixnodes_detailed().await?)
Ok(self.nym_api.get_active_mixnodes_detailed().await?)
}
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.validator_api.get_gateways().await?)
Ok(self.nym_api.get_gateways().await?)
}
pub async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
Ok(self.validator_api.blind_sign(request_body).await?)
Ok(self.nym_api.blind_sign(request_body).await?)
}
}
@@ -827,42 +827,40 @@ impl CoconutApiClient {
#[derive(Clone)]
pub struct ApiClient {
pub validator_api: validator_api::Client,
pub nym_api_client: nym_api::Client,
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
// we could re-implement the communication with the REST API on port 1317
}
impl ApiClient {
pub fn new(api_url: Url) -> Self {
let validator_api_client = validator_api::Client::new(api_url);
let nym_api_client = nym_api::Client::new(api_url);
ApiClient {
validator_api: validator_api_client,
}
ApiClient { nym_api_client }
}
pub fn change_validator_api(&mut self, new_endpoint: Url) {
self.validator_api.change_url(new_endpoint);
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api_client.change_url(new_endpoint);
}
pub async fn get_cached_active_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.validator_api.get_active_mixnodes().await?)
Ok(self.nym_api_client.get_active_mixnodes().await?)
}
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.validator_api.get_rewarded_mixnodes().await?)
Ok(self.nym_api_client.get_rewarded_mixnodes().await?)
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes().await?)
Ok(self.nym_api_client.get_mixnodes().await?)
}
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.validator_api.get_gateways().await?)
Ok(self.nym_api_client.get_gateways().await?)
}
pub async fn get_gateway_core_status_count(
@@ -871,7 +869,7 @@ impl ApiClient {
since: Option<i64>,
) -> Result<GatewayCoreStatusResponse, ValidatorClientError> {
Ok(self
.validator_api
.nym_api_client
.get_gateway_core_status_count(identity, since)
.await?)
}
@@ -882,7 +880,7 @@ impl ApiClient {
since: Option<i64>,
) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> {
Ok(self
.validator_api
.nym_api_client
.get_mixnode_core_status_count(mix_id, since)
.await?)
}
@@ -891,7 +889,7 @@ impl ApiClient {
&self,
mix_id: MixId,
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
Ok(self.validator_api.get_mixnode_status(mix_id).await?)
Ok(self.nym_api_client.get_mixnode_status(mix_id).await?)
}
pub async fn get_mixnode_reward_estimation(
@@ -899,7 +897,7 @@ impl ApiClient {
mix_id: MixId,
) -> Result<RewardEstimationResponse, ValidatorClientError> {
Ok(self
.validator_api
.nym_api_client
.get_mixnode_reward_estimation(mix_id)
.await?)
}
@@ -909,7 +907,7 @@ impl ApiClient {
mix_id: MixId,
) -> Result<StakeSaturationResponse, ValidatorClientError> {
Ok(self
.validator_api
.nym_api_client
.get_mixnode_stake_saturation(mix_id)
.await?)
}
@@ -918,7 +916,7 @@ impl ApiClient {
&self,
request_body: &BlindSignRequestBody,
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
Ok(self.validator_api.blind_sign(request_body).await?)
Ok(self.nym_api_client.blind_sign(request_body).await?)
}
pub async fn partial_bandwidth_credential(
@@ -926,7 +924,7 @@ impl ApiClient {
request_body: &str,
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
Ok(self
.validator_api
.nym_api_client
.partial_bandwidth_credential(request_body)
.await?)
}
@@ -936,7 +934,7 @@ impl ApiClient {
request_body: &VerifyCredentialBody,
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
Ok(self
.validator_api
.nym_api_client
.verify_bandwidth_credential(request_body)
.await?)
}
@@ -108,7 +108,7 @@ async fn test_nymd_connection(
log::debug!("Checking: nymd_url: {url}: {}: {}", "failed".red(), e);
false
}
Ok(Err(NymdError::AbciError { code, log, .. })) => {
Ok(Err(NymdError::AbciError(code, log))) => {
// We accept the mixnet contract not found as ok from a connection standpoint. This happens
// for example on a pre-launch network.
log::debug!(
@@ -1,15 +1,15 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::validator_api;
use crate::nym_api;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ValidatorClientError {
#[error("There was an issue with the validator api request - {source}")]
ValidatorAPIError {
NymAPIError {
#[from]
source: validator_api::error::ValidatorAPIError,
source: nym_api::error::NymAPIError,
},
#[error("One of the provided URLs was malformed - {0}")]
@@ -5,14 +5,14 @@ pub mod client;
#[cfg(feature = "nymd-client")]
pub mod connection_tester;
mod error;
pub mod nym_api;
#[cfg(feature = "nymd-client")]
pub mod nymd;
pub mod validator_api;
#[cfg(feature = "nymd-client")]
pub use crate::client::{ApiClient, CoconutApiClient};
pub use crate::error::ValidatorClientError;
pub use validator_api_requests::*;
pub use nym_api_requests::*;
#[cfg(feature = "nymd-client")]
pub use client::{Client, Config};
@@ -1,8 +1,8 @@
use nym_api_requests::models::RequestError;
use thiserror::Error;
use validator_api_requests::models::RequestError;
#[derive(Error, Debug)]
pub enum ValidatorAPIError {
pub enum NymAPIError {
#[error("There was an issue with the REST request - {source}")]
ReqwestClientError {
#[from]
@@ -12,6 +12,6 @@ pub enum ValidatorAPIError {
#[error("Request failed with error message - {0}")]
GenericRequestFailure(String),
#[error("The validator API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())]
#[error("The nym API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())]
ApiRequestFailure { status: u16, error: RequestError },
}
@@ -1,23 +1,22 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::validator_api::error::ValidatorAPIError;
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use crate::nym_api::error::NymAPIError;
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
use mixnet_contract_common::mixnode::MixNodeDetails;
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use nym_api_requests::models::{
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse,
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError,
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
};
use reqwest::Response;
use serde::{Deserialize, Serialize};
use url::Url;
use validator_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
};
use validator_api_requests::models::{
ComputeRewardEstParam, GatewayCoreStatusResponse, GatewayStatusReportResponse,
GatewayUptimeHistoryResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse, StakeSaturationResponse,
UptimeResponse,
};
pub mod error;
pub mod routes;
@@ -54,7 +53,7 @@ impl Client {
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<Response, ValidatorAPIError>
) -> Result<Response, NymAPIError>
where
K: AsRef<str>,
V: AsRef<str>,
@@ -63,11 +62,11 @@ impl Client {
Ok(self.reqwest_client.get(url).send().await?)
}
async fn query_validator_api<T, K, V>(
async fn query_nym_api<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, ValidatorAPIError>
) -> Result<T, NymAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
@@ -77,16 +76,16 @@ impl Client {
if res.status().is_success() {
Ok(res.json().await?)
} else {
Err(ValidatorAPIError::GenericRequestFailure(res.text().await?))
Err(NymAPIError::GenericRequestFailure(res.text().await?))
}
}
// This works for endpoints returning Result<Json<T>, ErrorResponse>
async fn query_validator_api_fallible<T, K, V>(
async fn query_nym_api_fallible<T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
) -> Result<T, ValidatorAPIError>
) -> Result<T, NymAPIError>
where
for<'a> T: Deserialize<'a>,
K: AsRef<str>,
@@ -98,19 +97,19 @@ impl Client {
Ok(res.json().await?)
} else {
let request_error: RequestError = res.json().await?;
Err(ValidatorAPIError::ApiRequestFailure {
Err(NymAPIError::ApiRequestFailure {
status: status.as_u16(),
error: request_error,
})
}
}
async fn post_validator_api<B, T, K, V>(
async fn post_nym_api<B, T, K, V>(
&self,
path: PathSegments<'_>,
params: Params<'_, K, V>,
json_body: &B,
) -> Result<T, ValidatorAPIError>
) -> Result<T, NymAPIError>
where
B: Serialize + ?Sized,
for<'a> T: Deserialize<'a>,
@@ -122,21 +121,17 @@ impl Client {
if response.status().is_success() {
Ok(response.json().await?)
} else {
Err(ValidatorAPIError::GenericRequestFailure(
response.text().await?,
))
Err(NymAPIError::GenericRequestFailure(response.text().await?))
}
}
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
.await
}
pub async fn get_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
self.query_validator_api(
pub async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -148,13 +143,13 @@ impl Client {
.await
}
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
.await
}
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
self.query_validator_api(
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
NO_PARAMS,
)
@@ -163,8 +158,8 @@ impl Client {
pub async fn get_active_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
self.query_validator_api(
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -177,8 +172,8 @@ impl Client {
.await
}
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorAPIError> {
self.query_validator_api(
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.query_nym_api(
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
NO_PARAMS,
)
@@ -188,8 +183,8 @@ impl Client {
pub async fn get_mixnode_report(
&self,
mix_id: MixId,
) -> Result<MixnodeStatusReportResponse, ValidatorAPIError> {
self.query_validator_api(
) -> Result<MixnodeStatusReportResponse, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -205,8 +200,8 @@ impl Client {
pub async fn get_gateway_report(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<GatewayStatusReportResponse, ValidatorAPIError> {
self.query_validator_api(
) -> Result<GatewayStatusReportResponse, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -222,8 +217,8 @@ impl Client {
pub async fn get_mixnode_history(
&self,
mix_id: MixId,
) -> Result<MixnodeUptimeHistoryResponse, ValidatorAPIError> {
self.query_validator_api(
) -> Result<MixnodeUptimeHistoryResponse, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -239,8 +234,8 @@ impl Client {
pub async fn get_gateway_history(
&self,
identity: IdentityKeyRef<'_>,
) -> Result<GatewayUptimeHistoryResponse, ValidatorAPIError> {
self.query_validator_api(
) -> Result<GatewayUptimeHistoryResponse, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -255,8 +250,8 @@ impl Client {
pub async fn get_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorAPIError> {
self.query_validator_api(
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
@@ -273,9 +268,9 @@ impl Client {
&self,
identity: IdentityKeyRef<'_>,
since: Option<i64>,
) -> Result<GatewayCoreStatusResponse, ValidatorAPIError> {
) -> Result<GatewayCoreStatusResponse, NymAPIError> {
if let Some(since) = since {
self.query_validator_api(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -287,7 +282,7 @@ impl Client {
)
.await
} else {
self.query_validator_api(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -304,9 +299,9 @@ impl Client {
&self,
mix_id: MixId,
since: Option<i64>,
) -> Result<MixnodeCoreStatusResponse, ValidatorAPIError> {
) -> Result<MixnodeCoreStatusResponse, NymAPIError> {
if let Some(since) = since {
self.query_validator_api(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -318,7 +313,7 @@ impl Client {
)
.await
} else {
self.query_validator_api(
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -335,8 +330,8 @@ impl Client {
pub async fn get_mixnode_status(
&self,
mix_id: MixId,
) -> Result<MixnodeStatusResponse, ValidatorAPIError> {
self.query_validator_api(
) -> Result<MixnodeStatusResponse, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -352,8 +347,8 @@ impl Client {
pub async fn get_mixnode_reward_estimation(
&self,
mix_id: MixId,
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
self.query_validator_api_fallible(
) -> Result<RewardEstimationResponse, NymAPIError> {
self.query_nym_api_fallible(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -366,30 +361,11 @@ impl Client {
.await
}
pub async fn compute_mixnode_reward_estimation(
&self,
mix_id: MixId,
request_body: &ComputeRewardEstParam,
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
self.post_validator_api(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
routes::MIXNODE,
&mix_id.to_string(),
routes::COMPUTE_REWARD_ESTIMATION,
],
NO_PARAMS,
request_body,
)
.await
}
pub async fn get_mixnode_stake_saturation(
&self,
mix_id: MixId,
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
self.query_validator_api_fallible(
) -> Result<StakeSaturationResponse, NymAPIError> {
self.query_nym_api_fallible(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -405,8 +381,8 @@ impl Client {
pub async fn get_mixnode_inclusion_probability(
&self,
mix_id: MixId,
) -> Result<InclusionProbabilityResponse, ValidatorAPIError> {
self.query_validator_api_fallible(
) -> Result<InclusionProbabilityResponse, NymAPIError> {
self.query_nym_api_fallible(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -422,8 +398,8 @@ impl Client {
pub async fn get_mixnode_avg_uptime(
&self,
mix_id: MixId,
) -> Result<UptimeResponse, ValidatorAPIError> {
self.query_validator_api_fallible(
) -> Result<UptimeResponse, NymAPIError> {
self.query_nym_api_fallible(
&[
routes::API_VERSION,
routes::STATUS_ROUTES,
@@ -439,8 +415,8 @@ impl Client {
pub async fn blind_sign(
&self,
request_body: &BlindSignRequestBody,
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
self.post_validator_api(
) -> Result<BlindedSignatureResponse, NymAPIError> {
self.post_nym_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
@@ -456,8 +432,8 @@ impl Client {
pub async fn partial_bandwidth_credential(
&self,
request_body: &str,
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
self.post_validator_api(
) -> Result<BlindedSignatureResponse, NymAPIError> {
self.post_nym_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
@@ -473,8 +449,8 @@ impl Client {
pub async fn verify_bandwidth_credential(
&self,
request_body: &VerifyCredentialBody,
) -> Result<VerifyCredentialResponse, ValidatorAPIError> {
self.post_validator_api(
) -> Result<VerifyCredentialResponse, NymAPIError> {
self.post_nym_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
@@ -1,9 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use network_defaults::VALIDATOR_API_VERSION;
use network_defaults::NYM_API_VERSION;
pub const API_VERSION: &str = VALIDATOR_API_VERSION;
pub const API_VERSION: &str = NYM_API_VERSION;
pub const MIXNODES: &str = "mixnodes";
pub const GATEWAYS: &str = "gateways";
@@ -28,7 +28,6 @@ pub const STATUS: &str = "status";
pub const REPORT: &str = "report";
pub const HISTORY: &str = "history";
pub const REWARD_ESTIMATION: &str = "reward-estimation";
pub const COMPUTE_REWARD_ESTIMATION: &str = "compute-reward-estimation";
pub const AVG_UPTIME: &str = "avg_uptime";
pub const STAKE_SATURATION: &str = "stake-saturation";
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nymd;
use crate::nymd::coin::Coin;
use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key};
use crate::nymd::cosmwasm_client::types::{
@@ -24,6 +23,7 @@ use cosmrs::rpc::endpoint::broadcast;
use cosmrs::rpc::endpoint::tx::Response as TxResponse;
use cosmrs::rpc::query::Query;
use cosmrs::rpc::{self, HttpClient, Order};
use cosmrs::tendermint::abci::Code as AbciCode;
use cosmrs::tendermint::abci::Transaction;
use cosmrs::tendermint::{abci, block, chain};
use cosmrs::{tx, AccountId, Coin as CosmosCoin, Tx};
@@ -66,9 +66,13 @@ pub trait CosmWasmClient: rpc::Client {
req.encode(&mut buf)?;
let res = self.abci_query(path, buf, None, false).await?;
let res_success = nymd::error::parse_abci_query_result(res)?;
Ok(Res::decode(res_success.value.as_ref())?)
match res.code {
AbciCode::Err(code) => return Err(NymdError::AbciError(code, res.log)),
AbciCode::Ok => (),
}
Ok(Res::decode(res.value.as_ref())?)
}
async fn get_chain_id(&self) -> Result<chain::Id, NymdError> {
@@ -2,23 +2,16 @@
// SPDX-License-Identifier: Apache-2.0
use crate::nymd::cosmwasm_client::types::ContractCodeId;
use cosmrs::{
bip32,
rpc::endpoint::abci_query::AbciQuery,
tendermint::{
abci::{self, Code as AbciCode},
block,
},
tx, AccountId,
};
use cosmrs::tendermint::{abci, block};
use cosmrs::{bip32, tx, AccountId};
use std::io;
use std::time::Duration;
use thiserror::Error;
use std::{io, time::Duration};
pub use cosmrs::rpc::{
error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail},
response_error::{Code, ResponseError},
pub use cosmrs::rpc::error::{
Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail,
};
pub use cosmrs::rpc::response_error::{Code, ResponseError};
#[derive(Debug, Error)]
pub enum NymdError {
@@ -117,12 +110,8 @@ pub enum NymdError {
#[error("Failed to estimate gas price for the transaction")]
GasEstimationFailure,
#[error("Abci query failed with code {code} - {log}")]
AbciError {
code: u32,
log: abci::Log,
pretty_log: Option<String>,
},
#[error("Abci query failed with code {0} - {1}")]
AbciError(u32, abci::Log),
#[error("Unsupported account type: {type_url}")]
UnsupportedAccountType { type_url: String },
@@ -146,32 +135,6 @@ pub enum NymdError {
UnexpectedBech32Prefix { got: String, expected: String },
}
// The purpose of parsing the abci query result is that we want to generate the `pretty_log` if
// possible.
pub fn parse_abci_query_result(query_result: AbciQuery) -> Result<AbciQuery, NymdError> {
match query_result.code {
AbciCode::Ok => Ok(query_result),
AbciCode::Err(code) => Err(NymdError::AbciError {
code,
log: query_result.log.clone(),
pretty_log: try_parse_abci_log(&query_result.log),
}),
}
}
// Some of the error strings returned by the query are a bit too technical to present to the
// enduser. So we special case some commonly encountered errors.
fn try_parse_abci_log(log: &abci::Log) -> Option<String> {
if log
.value()
.contains("Maximum amount of locked coins has already been pledged")
{
Some("Maximum amount of locked tokens has alredy been used. You can only use up to 10% of your locked tokens for bonding and delegating.".to_string())
} else {
None
}
}
impl NymdError {
pub fn is_tendermint_response_timeout(&self) -> bool {
match &self {
+2 -2
View File
@@ -16,9 +16,9 @@ pub enum ContextError {
#[error("failed to create client - {0}")]
NymdError(String),
#[error("{0}")]
#[error(transparent)]
NymdErrorPassthrough(#[from] validator_client::nymd::error::NymdError),
#[error("{0}")]
#[error(transparent)]
ValidatorClientError(#[from] validator_client::ValidatorClientError),
}
+14 -14
View File
@@ -7,8 +7,8 @@ use network_defaults::{
NymNetworkDetails,
};
use tap::prelude::*;
pub use validator_client::nym_api::Client as NymApiClient;
use validator_client::nymd::{self, AccountId, NymdClient, QueryNymdClient, SigningNymdClient};
pub use validator_client::validator_api::Client as ValidatorApiClient;
use crate::context::errors::ContextError;
@@ -16,14 +16,14 @@ pub mod errors;
pub type SigningClient = validator_client::nymd::NymdClient<SigningNymdClient>;
pub type QueryClient = validator_client::nymd::NymdClient<QueryNymdClient>;
pub type SigningClientWithValidatorAPI = validator_client::Client<SigningNymdClient>;
pub type QueryClientWithValidatorAPI = validator_client::Client<QueryNymdClient>;
pub type SigningClientWithNymd = validator_client::Client<SigningNymdClient>;
pub type QueryClientWithNymd = validator_client::Client<QueryNymdClient>;
#[derive(Debug)]
pub struct ClientArgs {
pub config_env_file: Option<std::path::PathBuf>,
pub nymd_url: Option<String>,
pub validator_api_url: Option<String>,
pub nym_api_url: Option<String>,
pub mnemonic: Option<bip39::Mnemonic>,
pub mixnet_contract_address: Option<AccountId>,
pub vesting_contract_address: Option<AccountId>,
@@ -38,8 +38,8 @@ pub fn get_network_details(args: &ClientArgs) -> Result<NymNetworkDetails, Conte
if let Some(nymd_url) = args.nymd_url.as_ref() {
std::env::set_var(NYMD_VALIDATOR, nymd_url);
}
if let Some(validator_api_url) = args.validator_api_url.as_ref() {
std::env::set_var(API_VALIDATOR, validator_api_url);
if let Some(nym_api_url) = args.nym_api_url.as_ref() {
std::env::set_var(API_VALIDATOR, nym_api_url);
}
if let Some(mixnet_contract_address) = args.mixnet_contract_address.as_ref() {
std::env::set_var(MIXNET_CONTRACT_ADDRESS, mixnet_contract_address.to_string());
@@ -59,7 +59,7 @@ pub fn create_signing_client(
network_details: &NymNetworkDetails,
) -> Result<SigningClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
.tap_err(|err| log::error!("Failed to get client config - {err}"))?;
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
@@ -88,7 +88,7 @@ pub fn create_query_client(
network_details: &NymNetworkDetails,
) -> Result<QueryClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
.tap_err(|err| log::error!("Failed to get client config - {err}"))?;
let nymd_url = network_details
.endpoints
@@ -103,12 +103,12 @@ pub fn create_query_client(
}
}
pub fn create_signing_client_with_validator_api(
pub fn create_signing_client_with_nym_api(
args: ClientArgs,
network_details: &NymNetworkDetails,
) -> Result<SigningClientWithValidatorAPI, ContextError> {
) -> Result<SigningClientWithNymd, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
.tap_err(|err| log::error!("Failed to get client config - {err}"))?;
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
@@ -126,11 +126,11 @@ pub fn create_signing_client_with_validator_api(
}
}
pub fn create_query_client_with_validator_api(
pub fn create_query_client_with_nym_api(
network_details: &NymNetworkDetails,
) -> Result<QueryClientWithValidatorAPI, ContextError> {
) -> Result<QueryClientWithNymd, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.tap_err(|e| log::error!("Failed to get client config - {:?}", e))?;
.tap_err(|err| log::error!("Failed to get client config - {err}"))?;
match validator_client::client::Client::new_query(client_config) {
Ok(client) => Ok(client),
@@ -4,7 +4,7 @@
use clap::Parser;
use log::info;
use crate::context::SigningClientWithValidatorAPI;
use crate::context::SigningClientWithNymd;
use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough};
use comfy_table::Table;
@@ -14,7 +14,7 @@ use mixnet_contract_common::{Delegation, PendingEpochEvent, PendingEpochEventKin
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) {
pub async fn execute(_args: Args, client: SigningClientWithNymd) {
info!(
"Getting delegations for account {}...",
client.nymd.address()
@@ -48,14 +48,14 @@ pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) {
}
}
async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithValidatorAPI) -> String {
async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithNymd) -> String {
match client.nymd.get_block_timestamp(Some(block_height)).await {
Ok(res) => res.to_rfc3339(),
Err(_e) => "-".to_string(),
}
}
async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientWithValidatorAPI) {
async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientWithNymd) {
let mut table = Table::new();
table.set_header(vec!["Timestamp", "Mix Id", "Delegation", "Proxy"]);
@@ -75,10 +75,7 @@ async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientW
println!("{table}");
}
async fn print_delegation_events(
events: Vec<PendingEpochEvent>,
client: &SigningClientWithValidatorAPI,
) {
async fn print_delegation_events(events: Vec<PendingEpochEvent>, client: &SigningClientWithNymd) {
let mut table = Table::new();
table.set_header(vec![
@@ -4,7 +4,7 @@
use clap::Parser;
use comfy_table::Table;
use crate::context::QueryClientWithValidatorAPI;
use crate::context::QueryClientWithNymd;
use crate::utils::{pretty_cosmwasm_coin, show_error};
#[derive(Debug, Parser)]
@@ -14,8 +14,8 @@ pub struct Args {
pub identity_key: Option<String>,
}
pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
match client.validator_api.get_gateways().await {
pub async fn query(args: Args, client: &QueryClientWithNymd) {
match client.nym_api.get_gateways().await {
Ok(res) => match args.identity_key {
Some(identity_key) => {
let node = res.iter().find(|node| {
@@ -4,7 +4,7 @@
use clap::Parser;
use comfy_table::Table;
use crate::context::QueryClientWithValidatorAPI;
use crate::context::QueryClientWithNymd;
use crate::utils::{pretty_decimal_with_denom, show_error};
#[derive(Debug, Parser)]
@@ -14,8 +14,8 @@ pub struct Args {
pub identity_key: Option<String>,
}
pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
match client.validator_api.get_mixnodes().await {
pub async fn query(args: Args, client: &QueryClientWithNymd) {
match client.nym_api.get_mixnodes().await {
Ok(res) => match args.identity_key {
Some(identity_key) => {
let node = res.iter().find(|node| {
@@ -11,6 +11,6 @@ pub enum Errors {
#[error("signature error - {0}")]
SignatureError(#[from] k256::ecdsa::signature::Error),
#[error("{0}")]
#[error(transparent)]
CosmrsError(#[from] cosmrs::ErrorReport),
}
+3 -3
View File
@@ -45,13 +45,13 @@ impl PersistentStorage {
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
Ok(db) => db,
Err(err) => {
error!("Failed to connect to SQLx database: {}", err);
error!("Failed to connect to SQLx database: {err}");
return Err(err.into());
}
};
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
error!("Failed to perform migration on the SQLx database: {}", err);
error!("Failed to perform migration on the SQLx database: {err}");
return Err(err.into());
}
@@ -104,7 +104,7 @@ impl Storage for PersistentStorage {
pub async fn initialise_storage(path: PathBuf) -> PersistentStorage {
match PersistentStorage::init(path).await {
Err(err) => panic!("failed to initialise credential storage - {}", err),
Err(err) => panic!("failed to initialise credential storage - {err}"),
Ok(storage) => storage,
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ thiserror = "1.0"
# I guess temporarily until we get serde support in coconut up and running
coconut-interface = { path = "../coconut-interface" }
crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] }
validator-api-requests = { path = "../../validator-api/validator-api-requests" }
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
validator-client = { path = "../client-libs/validator-client" }
[dev-dependencies]
+2 -2
View File
@@ -9,7 +9,7 @@ type Aes128Ctr = ctr::Ctr64LE<Aes128>;
/// Hashing algorithm used during hkdf for ephemeral shared key generation per blinded signature
/// response encryption.
pub type ValidatorApiCredentialHkdfAlgorithm = blake3::Hasher;
pub type NymApiCredentialHkdfAlgorithm = blake3::Hasher;
/// Encryption algorithm used for end-to-end encryption of blinded signature response
pub type ValidatorApiCredentialEncryptionAlgorithm = Aes128Ctr;
pub type NymApiCredentialEncryptionAlgorithm = Aes128Ctr;
+6 -8
View File
@@ -8,13 +8,11 @@ use coconut_interface::{
use crypto::asymmetric::encryption::PublicKey;
use crypto::shared_key::recompute_shared_key;
use crypto::symmetric::stream_cipher;
use validator_api_requests::coconut::BlindSignRequestBody;
use nym_api_requests::coconut::BlindSignRequestBody;
use validator_client::client::CoconutApiClient;
use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
use crate::coconut::params::{
ValidatorApiCredentialEncryptionAlgorithm, ValidatorApiCredentialHkdfAlgorithm,
};
use crate::coconut::params::{NymApiCredentialEncryptionAlgorithm, NymApiCredentialHkdfAlgorithm};
use crate::error::Error;
pub async fn obtain_aggregate_verification_key(
@@ -66,11 +64,11 @@ async fn obtain_partial_credential(
let remote_key = PublicKey::from_bytes(&response.remote_key)?;
let encryption_key = recompute_shared_key::<
ValidatorApiCredentialEncryptionAlgorithm,
ValidatorApiCredentialHkdfAlgorithm,
NymApiCredentialEncryptionAlgorithm,
NymApiCredentialHkdfAlgorithm,
>(&remote_key, attributes.encryption_key());
let zero_iv = stream_cipher::zero_iv::<ValidatorApiCredentialEncryptionAlgorithm>();
let blinded_signature_bytes = stream_cipher::decrypt::<ValidatorApiCredentialEncryptionAlgorithm>(
let zero_iv = stream_cipher::zero_iv::<NymApiCredentialEncryptionAlgorithm>();
let blinded_signature_bytes = stream_cipher::decrypt::<NymApiCredentialEncryptionAlgorithm>(
&encryption_key,
&zero_iv,
&encrypted_signature,
+1
View File
@@ -22,6 +22,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true }
serde_bytes = { version = "0.11.6", optional = true }
serde_crate = { version = "1.0", optional = true, default_features = false, package = "serde" }
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
thiserror = "1.0.37"
# internal
nymsphinx-types = { path = "../nymsphinx/types" }
-5
View File
@@ -326,11 +326,6 @@ pub fn try_recover_verification_keys(
return Err(DkgError::MismatchedDealings);
}
// currently we expect every dealer to also be a receiver. This restriction might be relaxed in the future
if dealings.len() != receivers.len() {
return Err(DkgError::MismatchedDealings);
}
let indices = receivers.keys().collect::<Vec<_>>();
// Compute A0, ..., A_{t-1}
+122
View File
@@ -254,3 +254,125 @@ fn full_threshold_secret_resharing() {
assert_ne!(derived_secrets, reshared_secrets);
assert_ne!(recovered_partials, reshared_partials);
}
#[test]
#[ignore] // expensive test
fn full_threshold_secret_resharing_left_party() {
let dummy_seed = [42u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let params = setup();
// the simplest possible case
let threshold = 2;
// the indices are going to get assigned externally, so for test sake, use non-consecutive ones
let mut node_indices = vec![15u64, 248, 33521];
let mut receivers = BTreeMap::new();
let mut full_keys = Vec::new();
for index in &node_indices {
let (dk, pk) = keygen(&params, &mut rng);
receivers.insert(*index, *pk.public_key());
full_keys.push((dk, pk))
}
let first_dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
})
.collect::<Vec<_>>();
// recover verification keys
let RecoveredVerificationKeys {
recovered_master: public_original_master,
recovered_partials,
} = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
let mut derived_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
let shares = first_dealings
.iter()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
let recovered_secret =
combine_shares(shares, &receivers.keys().copied().collect::<Vec<_>>()).unwrap();
derived_secrets.push(recovered_secret)
}
let original_master = perform_lagrangian_interpolation_at_origin(&[
(Scalar::from(node_indices[0]), derived_secrets[0]),
(Scalar::from(node_indices[1]), derived_secrets[1]),
])
.unwrap();
// one party leaves the process
let left_party_index = node_indices.pop().unwrap();
receivers.remove(&left_party_index);
full_keys.pop();
// and another one joins, but we're still over the threshold value of initial parties
let join_party_index = 100000;
let (dk, pk) = keygen(&params, &mut rng);
receivers.insert(join_party_index, *pk.public_key());
full_keys.push((dk, pk));
// only initial parties attempt to create resharing dealings!
let resharing_dealings = node_indices
.iter()
.zip(derived_secrets.iter().take(2))
.map(|(&dealer_index, prior_secret)| {
Dealing::create(
&mut rng,
&params,
dealer_index,
threshold,
&receivers,
Some(*prior_secret),
)
.0
})
.collect::<Vec<_>>();
for (reshared_dealing, prior_vk) in resharing_dealings
.iter()
.zip(recovered_partials.iter().take(2))
{
reshared_dealing
.verify(&params, threshold, &receivers, Some(*prior_vk))
.unwrap();
}
// recover verification keys
let RecoveredVerificationKeys {
recovered_master: public_reshared_master,
recovered_partials: reshared_partials,
} = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
let mut reshared_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
let shares = resharing_dealings
.iter()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
let recovered_secret = combine_shares(shares, &node_indices).unwrap();
reshared_secrets.push(recovered_secret)
}
let reshared_master = perform_lagrangian_interpolation_at_origin(&[
(Scalar::from(node_indices[0]), reshared_secrets[0]),
(Scalar::from(join_party_index), reshared_secrets[2]),
])
.unwrap();
// the master secret and public values didn't change
assert_eq!(original_master, reshared_master);
assert_eq!(public_original_master, public_reshared_master);
// but partials did
assert_ne!(derived_secrets, reshared_secrets);
assert_ne!(recovered_partials, reshared_partials);
}
+33 -26
View File
@@ -2,11 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
use pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use std::fmt::{self, Display, Formatter};
use thiserror::Error;
#[cfg(feature = "rand")]
use rand::{CryptoRng, RngCore};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{self, Display, Formatter};
/// Size of a X25519 private key
pub const PRIVATE_KEY_SIZE: usize = 32;
@@ -17,31 +19,26 @@ pub const PUBLIC_KEY_SIZE: usize = 32;
/// Size of a X25519 shared secret
pub const SHARED_SECRET_SIZE: usize = 32;
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
#[derive(Clone, Eq, PartialEq, Debug, Error)]
pub enum KeyRecoveryError {
InvalidPublicKeyBytes,
InvalidPrivateKeyBytes,
MalformedString(bs58::decode::Error),
}
#[error("received public key of invalid size. Got: {received}, expected: {expected}")]
InvalidSizePublicKey { received: usize, expected: usize },
impl From<bs58::decode::Error> for KeyRecoveryError {
fn from(err: bs58::decode::Error) -> Self {
KeyRecoveryError::MalformedString(err)
}
}
#[error("received private key of invalid size. Got: {received}, expected: {expected}")]
InvalidSizePrivateKey { received: usize, expected: usize },
// required for std::error::Error
impl Display for KeyRecoveryError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
KeyRecoveryError::InvalidPrivateKeyBytes => write!(f, "Invalid private key bytes"),
KeyRecoveryError::InvalidPublicKeyBytes => write!(f, "Invalid public key bytes"),
KeyRecoveryError::MalformedString(err) => write!(f, "malformed string - {}", err),
}
}
}
#[error("the base58 representation of the public key was malformed - {source}")]
MalformedPublicKeyString {
#[source]
source: bs58::decode::Error,
},
impl std::error::Error for KeyRecoveryError {}
#[error("the base58 representation of the private key was malformed - {source}")]
MalformedPrivateKeyString {
#[source]
source: bs58::decode::Error,
},
}
pub struct KeyPair {
pub(crate) private_key: PrivateKey,
@@ -112,7 +109,10 @@ impl PublicKey {
pub fn from_bytes(b: &[u8]) -> Result<Self, KeyRecoveryError> {
if b.len() != PUBLIC_KEY_SIZE {
return Err(KeyRecoveryError::InvalidPublicKeyBytes);
return Err(KeyRecoveryError::InvalidSizePublicKey {
received: b.len(),
expected: PUBLIC_KEY_SIZE,
});
}
let mut bytes = [0; PUBLIC_KEY_SIZE];
bytes.copy_from_slice(&b[..PUBLIC_KEY_SIZE]);
@@ -124,7 +124,9 @@ impl PublicKey {
}
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
let bytes = bs58::decode(val)
.into_vec()
.map_err(|source| KeyRecoveryError::MalformedPublicKeyString { source })?;
Self::from_bytes(&bytes)
}
}
@@ -188,7 +190,10 @@ impl PrivateKey {
pub fn from_bytes(b: &[u8]) -> Result<Self, KeyRecoveryError> {
if b.len() != PRIVATE_KEY_SIZE {
return Err(KeyRecoveryError::InvalidPrivateKeyBytes);
return Err(KeyRecoveryError::InvalidSizePrivateKey {
received: b.len(),
expected: PRIVATE_KEY_SIZE,
});
}
let mut bytes = [0; 32];
bytes.copy_from_slice(&b[..PRIVATE_KEY_SIZE]);
@@ -200,7 +205,9 @@ impl PrivateKey {
}
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, KeyRecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
let bytes = bs58::decode(val)
.into_vec()
.map_err(|source| KeyRecoveryError::MalformedPrivateKeyString { source })?;
Self::from_bytes(&bytes)
}
+30 -28
View File
@@ -6,10 +6,11 @@ pub use ed25519_dalek::SignatureError;
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
use nymsphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
use pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use std::fmt::{self, Display, Formatter};
use thiserror::Error;
#[cfg(feature = "rand")]
use rand::{CryptoRng, RngCore};
use std::fmt::{self, Display, Formatter};
#[cfg(feature = "serde")]
use serde::de::Error as SerdeError;
#[cfg(feature = "serde")]
@@ -17,35 +18,30 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde")]
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum Ed25519RecoveryError {
MalformedBytes(SignatureError),
MalformedString(bs58::decode::Error),
}
#[error(transparent)]
MalformedBytes(#[from] SignatureError),
impl From<SignatureError> for Ed25519RecoveryError {
fn from(err: SignatureError) -> Self {
Ed25519RecoveryError::MalformedBytes(err)
}
}
#[error("the base58 representation of the public key was malformed - {source}")]
MalformedPublicKeyString {
#[source]
source: bs58::decode::Error,
},
impl From<bs58::decode::Error> for Ed25519RecoveryError {
fn from(err: bs58::decode::Error) -> Self {
Ed25519RecoveryError::MalformedString(err)
}
}
#[error("the base58 representation of the private key was malformed - {source}")]
MalformedPrivateKeyString {
#[source]
source: bs58::decode::Error,
},
impl fmt::Display for Ed25519RecoveryError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Ed25519RecoveryError::MalformedBytes(err) => write!(f, "malformed bytes - {}", err),
Ed25519RecoveryError::MalformedString(err) => write!(f, "malformed string - {}", err),
}
}
#[error("the base58 representation of the signature was malformed - {source}")]
MalformedSignatureString {
#[source]
source: bs58::decode::Error,
},
}
impl std::error::Error for Ed25519RecoveryError {}
/// Keypair for usage in ed25519 EdDSA.
#[derive(Debug)]
pub struct KeyPair {
@@ -134,7 +130,9 @@ impl PublicKey {
}
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, Ed25519RecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
let bytes = bs58::decode(val)
.into_vec()
.map_err(|source| Ed25519RecoveryError::MalformedPublicKeyString { source })?;
Self::from_bytes(&bytes)
}
@@ -211,7 +209,9 @@ impl PrivateKey {
}
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, Ed25519RecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
let bytes = bs58::decode(val)
.into_vec()
.map_err(|source| Ed25519RecoveryError::MalformedPrivateKeyString { source })?;
Self::from_bytes(&bytes)
}
@@ -277,7 +277,9 @@ impl Signature {
}
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, Ed25519RecoveryError> {
let bytes = bs58::decode(val).into_vec()?;
let bytes = bs58::decode(val)
.into_vec()
.map_err(|source| Ed25519RecoveryError::MalformedSignatureString { source })?;
Self::from_bytes(&bytes)
}
+1
View File
@@ -16,6 +16,7 @@ serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.21.2", features = ["time", "macros", "rt", "net", "io-util"] }
tokio-util = { version = "0.7.3", features = ["codec"] }
url = "2.2"
thiserror = "1.0.37"
crypto = { path = "../crypto" }
nymsphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" }
@@ -4,63 +4,22 @@
use nymsphinx_acknowledgements::surb_ack::SurbAckRecoveryError;
use nymsphinx_addressing::nodes::NymNodeRoutingAddressError;
use nymsphinx_types::Error as SphinxError;
use std::fmt::{self, Display, Formatter};
use thiserror::Error;
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum MixProcessingError {
SphinxProcessingError(SphinxError),
InvalidHopAddress(NymNodeRoutingAddressError),
NoSurbAckInFinalHop,
MalformedSurbAck(SurbAckRecoveryError),
#[error("failed to process received packet: {0}")]
SphinxProcessingError(#[from] SphinxError),
#[error("the forward hop address was malformed: {0}")]
InvalidForwardHopAddress(#[from] NymNodeRoutingAddressError),
#[error("the final hop did not contain a SURB-Ack")]
NoSurbAckInFinalHop,
#[error("failed to recover the expected SURB-Ack packet: {0}")]
MalformedSurbAck(#[from] SurbAckRecoveryError),
#[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")]
ReceivedOldTypeVpnPacket,
}
impl From<SphinxError> for MixProcessingError {
// for the time being just have a single error instance for all possible results of SphinxError
fn from(err: SphinxError) -> Self {
use MixProcessingError::*;
SphinxProcessingError(err)
}
}
impl From<NymNodeRoutingAddressError> for MixProcessingError {
fn from(err: NymNodeRoutingAddressError) -> Self {
use MixProcessingError::*;
InvalidHopAddress(err)
}
}
impl From<SurbAckRecoveryError> for MixProcessingError {
fn from(err: SurbAckRecoveryError) -> Self {
use MixProcessingError::*;
MalformedSurbAck(err)
}
}
impl Display for MixProcessingError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
MixProcessingError::SphinxProcessingError(sphinx_err) => {
write!(f, "Sphinx Processing Error - {}", sphinx_err)
}
MixProcessingError::InvalidHopAddress(address_err) => {
write!(f, "Invalid Hop Address - {:?}", address_err)
}
MixProcessingError::NoSurbAckInFinalHop => {
write!(f, "No SURBAck present in the final hop data")
}
MixProcessingError::MalformedSurbAck(surb_ack_err) => {
write!(f, "Malformed SURBAck - {:?}", surb_ack_err)
}
MixProcessingError::ReceivedOldTypeVpnPacket => {
write!(f, "Received an old-type unsafe 'VPN' mode packet")
}
}
}
}
impl std::error::Error for MixProcessingError {}
@@ -51,7 +51,7 @@ impl SphinxPacketProcessor {
packet: SphinxPacket,
) -> Result<ProcessedPacket, MixProcessingError> {
packet.process(&self.sphinx_key).map_err(|err| {
debug!("Failed to unwrap Sphinx packet: {:?}", err);
debug!("Failed to unwrap Sphinx packet: {err}");
MixProcessingError::SphinxProcessingError(err)
})
}
+3 -3
View File
@@ -53,13 +53,13 @@ impl Display for RttError {
write!(f, "The received reply packet had invalid signature")
}
RttError::UnreachableNode(id, err) => {
write!(f, "Could not establish connection to {} - {}", id, err)
write!(f, "Could not establish connection to {} - {err}", id)
}
RttError::UnexpectedConnectionFailureWrite(id, err) => {
write!(f, "Failed to write echo packet to {} - {}", id, err)
write!(f, "Failed to write echo packet to {} - {err}", id)
}
RttError::UnexpectedConnectionFailureRead(id, err) => {
write!(f, "Failed to read reply packet from {} - {}", id, err)
write!(f, "Failed to read reply packet from {} - {err}", id)
}
RttError::ConnectionReadTimeout(id) => {
write!(f, "Timed out while trying to read reply packet from {}", id)
+7 -7
View File
@@ -11,7 +11,7 @@ use std::fmt::{Display, Formatter};
use std::net::SocketAddr;
use std::sync::Arc;
use std::{fmt, io, process};
use task::ShutdownListener;
use task::TaskClient;
use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream};
use tokio_util::codec::{Decoder, Encoder, Framed};
@@ -19,14 +19,14 @@ use tokio_util::codec::{Decoder, Encoder, Framed};
pub(crate) struct PacketListener {
address: SocketAddr,
connection_handler: Arc<ConnectionHandler>,
shutdown: ShutdownListener,
shutdown: TaskClient,
}
impl PacketListener {
pub(crate) fn new(
address: SocketAddr,
identity: Arc<identity::KeyPair>,
shutdown: ShutdownListener,
shutdown: TaskClient,
) -> Self {
PacketListener {
address,
@@ -66,7 +66,7 @@ impl PacketListener {
tokio::spawn(connection_handler.handle_connection(socket, remote_addr, handler_shutdown_listener));
}
Err(err) => warn!("Failed to accept incoming connection - {:?}", err),
Err(err) => warn!("Failed to accept incoming connection - {err}"),
}
},
_ = shutdown_listener.recv() => {
@@ -91,7 +91,7 @@ impl ConnectionHandler {
self: Arc<Self>,
conn: TcpStream,
remote: SocketAddr,
mut shutdown_listener: ShutdownListener,
mut shutdown_listener: TaskClient,
) {
debug!("Starting connection handler for {:?}", remote);
@@ -141,9 +141,9 @@ enum EchoPacketCodecError {
impl Display for EchoPacketCodecError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
EchoPacketCodecError::IoError(err) => write!(f, "encountered io error - {}", err),
EchoPacketCodecError::IoError(err) => write!(f, "encountered io error - {err}"),
EchoPacketCodecError::PacketRecoveryError(err) => {
write!(f, "failed to correctly decode an echo packet - {}", err)
write!(f, "failed to correctly decode an echo packet - {err}")
}
}
}
+16 -19
View File
@@ -12,7 +12,7 @@ use rand::thread_rng;
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::Arc;
use std::time::Duration;
use task::ShutdownListener;
use task::TaskClient;
use tokio::task::JoinHandle;
use tokio::time::sleep;
use url::Url;
@@ -69,8 +69,8 @@ pub struct Config {
/// due to being unable to get the list of nodes.
retry_timeout: Duration,
/// URLs to the validator apis for obtaining network topology.
validator_api_urls: Vec<Url>,
/// URLs to the nym apis for obtaining network topology.
nym_api_urls: Vec<Url>,
}
impl Config {
@@ -132,15 +132,15 @@ impl ConfigBuilder {
self
}
pub fn validator_api_urls(mut self, validator_api_urls: Vec<Url>) -> Self {
self.0.validator_api_urls = validator_api_urls;
pub fn nym_api_urls(mut self, nym_api_urls: Vec<Url>) -> Self {
self.0.nym_api_urls = nym_api_urls;
self
}
pub fn build(self) -> Config {
// panics here are fine as those are only ever constructed at the initial setup
assert!(
!self.0.validator_api_urls.is_empty(),
!self.0.nym_api_urls.is_empty(),
"at least one validator endpoint must be provided",
);
self.0
@@ -159,7 +159,7 @@ impl Default for ConfigBuilder {
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
testing_interval: DEFAULT_TESTING_INTERVAL,
retry_timeout: DEFAULT_RETRY_TIMEOUT,
validator_api_urls: vec![],
nym_api_urls: vec![],
})
}
}
@@ -168,7 +168,7 @@ pub struct VerlocMeasurer {
config: Config,
packet_sender: Arc<PacketSender>,
packet_listener: Arc<PacketListener>,
shutdown_listener: ShutdownListener,
shutdown_listener: TaskClient,
currently_used_api: usize,
@@ -184,9 +184,9 @@ impl VerlocMeasurer {
pub fn new(
mut config: Config,
identity: Arc<identity::KeyPair>,
shutdown_listener: ShutdownListener,
shutdown_listener: TaskClient,
) -> Self {
config.validator_api_urls.shuffle(&mut thread_rng());
config.nym_api_urls.shuffle(&mut thread_rng());
VerlocMeasurer {
packet_sender: Arc::new(PacketSender::new(
@@ -204,24 +204,21 @@ impl VerlocMeasurer {
)),
shutdown_listener,
currently_used_api: 0,
validator_client: validator_client::ApiClient::new(
config.validator_api_urls[0].clone(),
),
validator_client: validator_client::ApiClient::new(config.nym_api_urls[0].clone()),
config,
results: AtomicVerlocResult::new(),
}
}
fn use_next_validator_api(&mut self) {
if self.config.validator_api_urls.len() == 1 {
fn use_next_nym_api(&mut self) {
if self.config.nym_api_urls.len() == 1 {
warn!("There's only a single validator API available - it won't be possible to use a different one");
return;
}
self.currently_used_api =
(self.currently_used_api + 1) % self.config.validator_api_urls.len();
self.currently_used_api = (self.currently_used_api + 1) % self.config.nym_api_urls.len();
self.validator_client
.change_validator_api(self.config.validator_api_urls[self.currently_used_api].clone())
.change_nym_api(self.config.nym_api_urls[self.currently_used_api].clone())
}
pub fn get_verloc_results_pointer(&self) -> AtomicVerlocResult {
@@ -308,7 +305,7 @@ impl VerlocMeasurer {
"failed to obtain list of mixnodes from the validator - {}. Going to attempt to use another validator API in the next run",
err
);
self.use_next_validator_api();
self.use_next_nym_api();
sleep(self.config.retry_timeout).await;
continue;
}
+3 -3
View File
@@ -11,7 +11,7 @@ use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use std::{fmt, io};
use task::ShutdownListener;
use task::TaskClient;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio::time::sleep;
@@ -45,7 +45,7 @@ pub(crate) struct PacketSender {
packet_timeout: Duration,
connection_timeout: Duration,
delay_between_packets: Duration,
shutdown_listener: ShutdownListener,
shutdown_listener: TaskClient,
}
impl PacketSender {
@@ -55,7 +55,7 @@ impl PacketSender {
packet_timeout: Duration,
connection_timeout: Duration,
delay_between_packets: Duration,
shutdown_listener: ShutdownListener,
shutdown_listener: TaskClient,
) -> Self {
PacketSender {
identity,
+4 -4
View File
@@ -355,14 +355,14 @@ pub const DEFAULT_WEBSOCKET_LISTENING_PORT: u16 = 1977;
// 'SOCKS5' CLIENT
pub const DEFAULT_SOCKS5_LISTENING_PORT: u16 = 1080;
// VALIDATOR-API
pub const DEFAULT_VALIDATOR_API_PORT: u16 = 8080;
// NYM-API
pub const DEFAULT_NYM_API_PORT: u16 = 8080;
pub const VALIDATOR_API_VERSION: &str = "v1";
pub const NYM_API_VERSION: &str = "v1";
// REWARDING
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have reliable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
// pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$
// pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 55_556; // 40$/1hr at 1 Nym == 1$
// pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 9259; // 40$/1hr/6 at 1 Nym == 1$
@@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
thiserror = "1.0.37"
crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
nymsphinx-addressing = { path = "../addressing" }
@@ -4,10 +4,13 @@
use crate::identifier::prepare_identifier;
use crate::AckKey;
use nymsphinx_addressing::clients::Recipient;
use nymsphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
use nymsphinx_addressing::nodes::{
NymNodeRoutingAddress, NymNodeRoutingAddressError, MAX_NODE_ADDRESS_UNPADDED_LEN,
};
use nymsphinx_params::packet_sizes::PacketSize;
use nymsphinx_params::DEFAULT_NUM_MIX_HOPS;
use nymsphinx_types::builder::SphinxPacketBuilder;
use nymsphinx_types::Error as SphinxError;
use nymsphinx_types::{
delays::{self, Delay},
SphinxPacket,
@@ -15,6 +18,7 @@ use nymsphinx_types::{
use rand::{CryptoRng, RngCore};
use std::convert::TryFrom;
use std::time;
use thiserror::Error;
use topology::{NymTopology, NymTopologyError};
pub struct SurbAck {
@@ -23,11 +27,16 @@ pub struct SurbAck {
expected_total_delay: Delay,
}
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum SurbAckRecoveryError {
InvalidPacketSize,
InvalidAddress,
InvalidSphinxPacket,
#[error("received an invalid number of bytes to deserialize the SURB-Ack. Got {received}, expected {expected}")]
InvalidPacketSize { received: usize, expected: usize },
#[error("could not extract first hop address information - {0}")]
InvalidAddress(#[from] NymNodeRoutingAddressError),
#[error("the contained sphinx packet was not correctly formed - {0}")]
InvalidSphinxPacket(#[from] SphinxError),
}
impl SurbAck {
@@ -92,20 +101,17 @@ impl SurbAck {
b: &[u8],
) -> Result<(NymNodeRoutingAddress, SphinxPacket), SurbAckRecoveryError> {
if b.len() != Self::len() {
Err(SurbAckRecoveryError::InvalidPacketSize)
Err(SurbAckRecoveryError::InvalidPacketSize {
received: b.len(),
expected: Self::len(),
})
} else {
let address = match NymNodeRoutingAddress::try_from_bytes(b) {
Ok(address) => address,
Err(_) => return Err(SurbAckRecoveryError::InvalidAddress),
};
let address = NymNodeRoutingAddress::try_from_bytes(b)?;
// TODO: this will be variable once/if we decide to introduce optimization described
// in common/nymsphinx/chunking/src/lib.rs:available_plaintext_size()
let address_offset = MAX_NODE_ADDRESS_UNPADDED_LEN;
let packet = match SphinxPacket::from_bytes(&b[address_offset..]) {
Ok(packet) => packet,
Err(_) => return Err(SurbAckRecoveryError::InvalidSphinxPacket),
};
let packet = SphinxPacket::from_bytes(&b[address_offset..])?;
Ok((address, packet))
}
+1
View File
@@ -9,6 +9,7 @@ edition = "2021"
[dependencies]
bytes = "1.0"
tokio-util = { version = "0.7.3", features = ["codec"] }
thiserror = "1.0.37"
nymsphinx-types = { path = "../types" }
nymsphinx-params = { path = "../params" }
+24 -39
View File
@@ -1,57 +1,48 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::packet::{FramedSphinxPacket, Header};
use bytes::{Buf, BufMut, BytesMut};
use nymsphinx_params::packet_modes::InvalidPacketMode;
use nymsphinx_params::packet_sizes::{InvalidPacketSize, PacketSize};
use nymsphinx_types::Error as SphinxError;
use nymsphinx_types::SphinxPacket;
use std::io;
use thiserror::Error;
use tokio_util::codec::{Decoder, Encoder};
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum SphinxCodecError {
InvalidPacketSize,
InvalidPacketMode,
MalformedSphinxPacket,
IoError(io::Error),
}
#[error("the packet size information was malformed - {0}")]
InvalidPacketSize(#[from] InvalidPacketSize),
impl From<io::Error> for SphinxCodecError {
fn from(err: io::Error) -> Self {
SphinxCodecError::IoError(err)
}
#[error("the packet mode information was malformed - {0}")]
InvalidPacketMode(#[from] InvalidPacketMode),
#[error("the actual sphinx packet was malformed - {0}")]
MalformedSphinxPacket(#[from] SphinxError),
#[error("encountered an IO error - {0}")]
IoError(#[from] io::Error),
}
impl From<SphinxCodecError> for io::Error {
fn from(err: SphinxCodecError) -> Self {
match err {
SphinxCodecError::InvalidPacketSize => {
io::Error::new(io::ErrorKind::InvalidInput, "invalid packet size")
SphinxCodecError::InvalidPacketSize(source) => {
io::Error::new(io::ErrorKind::InvalidInput, source)
}
SphinxCodecError::InvalidPacketMode => {
io::Error::new(io::ErrorKind::InvalidInput, "invalid packet mode")
SphinxCodecError::InvalidPacketMode(source) => {
io::Error::new(io::ErrorKind::InvalidInput, source)
}
SphinxCodecError::MalformedSphinxPacket => {
io::Error::new(io::ErrorKind::InvalidData, "malformed packet")
SphinxCodecError::MalformedSphinxPacket(source) => {
io::Error::new(io::ErrorKind::InvalidData, source)
}
SphinxCodecError::IoError(err) => err,
}
}
}
impl From<InvalidPacketSize> for SphinxCodecError {
fn from(_: InvalidPacketSize) -> Self {
SphinxCodecError::InvalidPacketSize
}
}
impl From<InvalidPacketMode> for SphinxCodecError {
fn from(_: InvalidPacketMode) -> Self {
SphinxCodecError::InvalidPacketMode
}
}
// TODO: in the future it could be extended to have state containing symmetric encryption key
// so that all data could be encrypted easily (alternatively we could just slap TLS)
pub struct SphinxCodec;
@@ -97,17 +88,11 @@ impl Decoder for SphinxCodec {
// advance buffer past the header - at this point we have enough bytes
src.advance(header.size());
let sphinx_packet_bytes = src.split_to(sphinx_packet_size);
let sphinx_packet = match SphinxPacket::from_bytes(&sphinx_packet_bytes) {
Ok(sphinx_packet) => sphinx_packet,
// here it could be debatable whether stream is corrupt or not,
// but let's go with the safer approach and assume it is.
Err(_) => return Err(SphinxCodecError::MalformedSphinxPacket),
};
let nymsphinx_packet = FramedSphinxPacket {
header,
packet: sphinx_packet,
};
// here it could be debatable whether stream is corrupt or not,
// but let's go with the safer approach and assume it is.
let packet = SphinxPacket::from_bytes(&sphinx_packet_bytes)?;
let nymsphinx_packet = FramedSphinxPacket { header, packet };
// As per docs:
// Before returning from the function, implementations should ensure that the buffer
+2
View File
@@ -7,5 +7,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror = "1.0.37"
crypto = { path = "../../crypto", features = ["hashing", "symmetric"] }
nymsphinx-types = { path = "../types" }
+9 -5
View File
@@ -1,10 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryFrom;
use thiserror::Error;
#[derive(Debug)]
pub struct InvalidPacketMode;
#[derive(Error, Debug)]
#[error("{received} is not a valid packet mode tag")]
pub struct InvalidPacketMode {
received: u8,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -31,11 +35,11 @@ impl PacketMode {
impl TryFrom<u8> for PacketMode {
type Error = InvalidPacketMode;
fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
_ if value == (PacketMode::Mix as u8) => Ok(Self::Mix),
_ if value == (PacketMode::Vpn as u8) => Ok(Self::Vpn),
_ => Err(InvalidPacketMode),
v => Err(InvalidPacketMode { received: v }),
}
}
}
+18 -9
View File
@@ -6,6 +6,7 @@ use nymsphinx_types::header::HEADER_SIZE;
use nymsphinx_types::PAYLOAD_OVERHEAD_SIZE;
use std::convert::TryFrom;
use std::str::FromStr;
use thiserror::Error;
// it's up to the smart people to figure those values out : )
const REGULAR_PACKET_SIZE: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 2 * 1024;
@@ -20,11 +21,17 @@ const EXTENDED_PACKET_SIZE_8: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 8 *
const EXTENDED_PACKET_SIZE_16: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 16 * 1024;
const EXTENDED_PACKET_SIZE_32: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE + 32 * 1024;
#[derive(Debug)]
pub struct InvalidPacketSize;
#[derive(Debug, Error)]
pub enum InvalidPacketSize {
#[error("{received} is not a valid packet size tag")]
UnknownPacketTag { received: u8 },
#[derive(Debug)]
pub struct InvalidExtendedPacketSize;
#[error("{received} is not a valid extended packet size variant")]
UnknownExtendedPacketVariant { received: String },
#[error("{received} does not correspond with any known packet size")]
UnknownPacketSize { received: usize },
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -55,7 +62,9 @@ impl FromStr for PacketSize {
"extended8" => Ok(Self::ExtendedPacket8),
"extended16" => Ok(Self::ExtendedPacket16),
"extended32" => Ok(Self::ExtendedPacket32),
_ => Err(InvalidPacketSize),
s => Err(InvalidPacketSize::UnknownExtendedPacketVariant {
received: s.to_string(),
}),
}
}
}
@@ -63,14 +72,14 @@ impl FromStr for PacketSize {
impl TryFrom<u8> for PacketSize {
type Error = InvalidPacketSize;
fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
_ if value == (PacketSize::RegularPacket as u8) => Ok(Self::RegularPacket),
_ if value == (PacketSize::AckPacket as u8) => Ok(Self::AckPacket),
_ if value == (PacketSize::ExtendedPacket8 as u8) => Ok(Self::ExtendedPacket8),
_ if value == (PacketSize::ExtendedPacket16 as u8) => Ok(Self::ExtendedPacket16),
_ if value == (PacketSize::ExtendedPacket32 as u8) => Ok(Self::ExtendedPacket32),
_ => Err(InvalidPacketSize),
v => Err(InvalidPacketSize::UnknownPacketTag { received: v }),
}
}
}
@@ -94,7 +103,7 @@ impl PacketSize {
self.size() - HEADER_SIZE
}
pub fn get_type(size: usize) -> std::result::Result<Self, InvalidPacketSize> {
pub fn get_type(size: usize) -> Result<Self, InvalidPacketSize> {
if PacketSize::RegularPacket.size() == size {
Ok(PacketSize::RegularPacket)
} else if PacketSize::AckPacket.size() == size {
@@ -106,7 +115,7 @@ impl PacketSize {
} else if PacketSize::ExtendedPacket32.size() == size {
Ok(PacketSize::ExtendedPacket32)
} else {
Err(InvalidPacketSize)
Err(InvalidPacketSize::UnknownPacketSize { received: size })
}
}

Some files were not shown because too many files have changed in this diff Show More