Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb929b251e | |||
| b927acb35a | |||
| 9131a551d5 | |||
| 41aa391d9d | |||
| f44e17489d | |||
| e3f893f4c4 | |||
| 2dffa07be1 | |||
| 69e8b12328 | |||
| 46b59cdb09 | |||
| ad860303e8 | |||
| 16e1a154e0 |
@@ -18,6 +18,7 @@ pub mod acquire;
|
||||
pub mod error;
|
||||
mod utils;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BandwidthController<C, St> {
|
||||
storage: St,
|
||||
client: C,
|
||||
|
||||
@@ -201,7 +201,7 @@ where
|
||||
log::debug!("Setting up gateway");
|
||||
match setup {
|
||||
GatewaySetup::MustLoad { gateway_id } => {
|
||||
log::trace!("GatewaySetup::MustLoad with id: {gateway_id:?}");
|
||||
log::debug!("GatewaySetup::MustLoad with id: {gateway_id:?}");
|
||||
use_loaded_gateway_details(key_store, details_store, gateway_id).await
|
||||
}
|
||||
GatewaySetup::New {
|
||||
@@ -209,7 +209,7 @@ where
|
||||
available_gateways,
|
||||
wg_tun_address,
|
||||
} => {
|
||||
log::trace!("GatewaySetup::New with spec: {specification:?}");
|
||||
log::debug!("GatewaySetup::New with spec: {specification:?}");
|
||||
setup_new_gateway(
|
||||
key_store,
|
||||
details_store,
|
||||
@@ -224,7 +224,7 @@ where
|
||||
gateway_details,
|
||||
client_keys: managed_keys,
|
||||
} => {
|
||||
log::trace!("GatewaySetup::ReuseConnection");
|
||||
log::debug!("GatewaySetup::ReuseConnection");
|
||||
Ok(reuse_gateway_connection(
|
||||
authenticated_ephemeral_client,
|
||||
*gateway_details,
|
||||
|
||||
@@ -79,6 +79,7 @@ impl GatewayConfig {
|
||||
}
|
||||
|
||||
// TODO: this should be refactored into a state machine that keeps track of its authentication state
|
||||
#[derive(Debug)]
|
||||
pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
authenticated: bool,
|
||||
disabled_credentials_mode: bool,
|
||||
@@ -849,6 +850,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
// type alias for an ease of use
|
||||
pub type InitGatewayClient = GatewayClient<InitOnly>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InitOnly;
|
||||
|
||||
impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
|
||||
@@ -68,6 +68,7 @@ fn maybe_log_bandwidth(remaining: i64) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PartiallyDelegated {
|
||||
sink_half: SplitSink<WsConn, Message>,
|
||||
delegated_stream: (SplitStreamReceiver, oneshot::Sender<()>),
|
||||
@@ -262,6 +263,7 @@ impl PartiallyDelegated {
|
||||
// we can either have the stream itself or an option to re-obtain it
|
||||
// by notifying the future owning it to finish the execution and awaiting the result
|
||||
// which should be almost immediate (or an invalid state which should never, ever happen)
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum SocketState {
|
||||
Available(Box<WsConn>),
|
||||
PartiallyDelegated(PartiallyDelegated),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use crate::backends::memory::CoconutCredentialManager;
|
||||
use crate::error::StorageError;
|
||||
use crate::models::{StorableIssuedCredential, StoredIssuedCredential};
|
||||
@@ -23,6 +25,12 @@ impl Default for EphemeralStorage {
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for EphemeralStorage {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "EphemeralStorage")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Storage for EphemeralStorage {
|
||||
type StorageError = StorageError;
|
||||
|
||||
@@ -6,6 +6,7 @@ use futures::{Sink, Stream};
|
||||
use gloo_net::websocket::futures::WebSocket;
|
||||
use gloo_net::websocket::{Message, WebSocketError};
|
||||
use gloo_utils::errors::JsError;
|
||||
use std::fmt::{self, Formatter};
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
@@ -77,6 +78,12 @@ impl Stream for JSWebsocket {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for JSWebsocket {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "JSWebSocket")
|
||||
}
|
||||
}
|
||||
|
||||
impl Sink<WsMessage> for JSWebsocket {
|
||||
type Error = WsError;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ mod socks5_client;
|
||||
mod traits;
|
||||
|
||||
pub use client::{DisconnectedMixnetClient, IncludedSurbs, MixnetClientBuilder};
|
||||
pub use config::{Config, KeyMode};
|
||||
pub use config::Config;
|
||||
pub use native_client::MixnetClient;
|
||||
pub use native_client::MixnetClientSender;
|
||||
pub use nym_client_core::{
|
||||
|
||||
@@ -10,8 +10,11 @@ use crate::NymNetworkDetails;
|
||||
use crate::{Error, Result};
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::warn;
|
||||
use nym_client_core::client::base_client::storage::helpers::get_all_registered_identities;
|
||||
use log::{debug, warn};
|
||||
use nym_client_core::client::base_client::storage::helpers::{
|
||||
get_active_gateway_identity, get_all_registered_identities, has_gateway_details,
|
||||
set_active_gateway,
|
||||
};
|
||||
use nym_client_core::client::base_client::storage::{
|
||||
Ephemeral, GatewaysDetailsStore, MixnetClientStorage, OnDiskPersistent,
|
||||
};
|
||||
@@ -23,6 +26,7 @@ use nym_client_core::client::{
|
||||
use nym_client_core::config::DebugConfig;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::setup_gateway;
|
||||
use nym_client_core::init::types::{GatewaySelectionSpecification, GatewaySetup};
|
||||
use nym_network_defaults::WG_TUN_DEVICE_IP_ADDRESS;
|
||||
use nym_socks5_client_core::config::Socks5;
|
||||
@@ -248,13 +252,15 @@ where
|
||||
|
||||
/// Construct a [`DisconnectedMixnetClient`] from the setup specified.
|
||||
pub fn build(self) -> Result<DisconnectedMixnetClient<S>> {
|
||||
let client = DisconnectedMixnetClient::new(self.config, self.socks5_config, self.storage)?
|
||||
.custom_gateway_transceiver(self.custom_gateway_transceiver)
|
||||
.custom_topology_provider(self.custom_topology_provider)
|
||||
.custom_shutdown(self.custom_shutdown)
|
||||
.wireguard_mode(self.wireguard_mode)
|
||||
.wait_for_gateway(self.wait_for_gateway)
|
||||
.force_tls(self.force_tls);
|
||||
let mut client =
|
||||
DisconnectedMixnetClient::new(self.config, self.socks5_config, self.storage)?;
|
||||
|
||||
client.custom_gateway_transceiver = self.custom_gateway_transceiver;
|
||||
client.custom_topology_provider = self.custom_topology_provider;
|
||||
client.custom_shutdown = self.custom_shutdown;
|
||||
client.wireguard_mode = self.wireguard_mode;
|
||||
client.wait_for_gateway = self.wait_for_gateway;
|
||||
client.force_tls = self.force_tls;
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
@@ -355,48 +361,6 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn custom_shutdown(mut self, shutdown: Option<TaskClient>) -> Self {
|
||||
self.custom_shutdown = shutdown;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn custom_topology_provider(
|
||||
mut self,
|
||||
provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
) -> Self {
|
||||
self.custom_topology_provider = provider;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn custom_gateway_transceiver(
|
||||
mut self,
|
||||
gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send + Sync>>,
|
||||
) -> Self {
|
||||
self.custom_gateway_transceiver = gateway_transceiver;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn wireguard_mode(mut self, wireguard_mode: bool) -> Self {
|
||||
self.wireguard_mode = wireguard_mode;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn wait_for_gateway(mut self, wait_for_gateway: bool) -> Self {
|
||||
self.wait_for_gateway = wait_for_gateway;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn force_tls(mut self, must_use_tls: bool) -> Self {
|
||||
self.force_tls = must_use_tls;
|
||||
self
|
||||
}
|
||||
|
||||
fn get_api_endpoints(&self) -> Vec<Url> {
|
||||
self.config
|
||||
.network_details
|
||||
@@ -426,6 +390,65 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn setup_client_keys(&self) -> Result<()> {
|
||||
let mut rng = OsRng;
|
||||
let key_store = self.storage.key_store();
|
||||
|
||||
if key_store.load_keys().await.is_err() {
|
||||
debug!("Generating new client keys");
|
||||
nym_client_core::init::generate_new_client_keys(&mut rng, key_store).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn print_all_registered_gateway_identities(&self) {
|
||||
match get_all_registered_identities(self.storage.gateway_details_store()).await {
|
||||
Err(err) => {
|
||||
warn!("failed to query for all registered gateways: {err}")
|
||||
}
|
||||
Ok(all_ids) => {
|
||||
if !all_ids.is_empty() {
|
||||
debug!("this client is already registered with the following gateways:");
|
||||
for id in all_ids {
|
||||
debug!("{id}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn print_selected_gateway(&self) {
|
||||
match self.storage.gateway_details_store().active_gateway().await {
|
||||
Err(err) => {
|
||||
warn!("failed to query for the current active gateway: {err}")
|
||||
}
|
||||
Ok(active) => {
|
||||
if let Some(active) = active.registration {
|
||||
let id = active.details.gateway_id();
|
||||
debug!("currently selected gateway: {0}", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_active_gateway_if_previously_registered(
|
||||
&self,
|
||||
user_chosen_gateway: &str,
|
||||
) -> Result<bool> {
|
||||
let storage = self.storage.gateway_details_store();
|
||||
// Stricly speaking, `set_active_gateway` does this check internally as well, but since the
|
||||
// error is boxed away and we're using a generic storage, it's not so easy to match on it.
|
||||
// This function is at least less likely to fail on something unrelated to the existence of
|
||||
// the gateway in the set of registered gateways
|
||||
if has_gateway_details(storage, user_chosen_gateway).await? {
|
||||
set_active_gateway(storage, user_chosen_gateway).await?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
async fn new_gateway_setup(&self) -> Result<GatewaySetup, ClientCoreError> {
|
||||
let nym_api_endpoints = self.get_api_endpoints();
|
||||
|
||||
@@ -445,67 +468,67 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the client already has an active gateway enabled.
|
||||
async fn has_active_gateway(&self) -> bool {
|
||||
let storage = self.storage.gateway_details_store();
|
||||
|
||||
match storage.active_gateway().await {
|
||||
Err(err) => {
|
||||
warn!("failed to query for the current active gateway: {err}");
|
||||
return false;
|
||||
}
|
||||
Ok(active) => {
|
||||
if active.registration.is_some() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match get_all_registered_identities(storage).await {
|
||||
Err(err) => {
|
||||
warn!("failed to query for all registered gateways: {err}")
|
||||
}
|
||||
Ok(all_ids) => {
|
||||
if !all_ids.is_empty() {
|
||||
warn!("this client doesn't have an active gateway set, however, it's already registered with the following gateways (consider making one of them active):");
|
||||
for id in all_ids {
|
||||
warn!("{id}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Register with a gateway. If a gateway is provided in the config then that will try to be
|
||||
/// used. If none is specified, a gateway at random will be picked.
|
||||
/// used. If none is specified, a gateway at random will be picked. The used gateway is saved
|
||||
/// as the active gateway.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if you try to re-register when in an already registered
|
||||
/// state.
|
||||
pub async fn register_and_authenticate_gateway(&mut self) -> Result<()> {
|
||||
pub async fn setup_gateway(&mut self) -> Result<()> {
|
||||
if !matches!(self.state, BuilderState::New) {
|
||||
return Err(Error::ReregisteringGatewayNotSupported);
|
||||
}
|
||||
|
||||
log::debug!("Registering with gateway");
|
||||
self.print_all_registered_gateway_identities().await;
|
||||
self.print_selected_gateway().await;
|
||||
|
||||
let gateway_setup = if self.has_active_gateway().await {
|
||||
GatewaySetup::MustLoad { gateway_id: None }
|
||||
} else {
|
||||
self.new_gateway_setup().await?
|
||||
// Try to set active gateway to the same as the user chosen one, if it's in the set of
|
||||
// gateways that is already registered.
|
||||
if let Some(ref user_chosen_gateway) = self.config.user_chosen_gateway {
|
||||
if self
|
||||
.set_active_gateway_if_previously_registered(user_chosen_gateway)
|
||||
.await?
|
||||
{
|
||||
debug!("user chosen gateway is already registered, set as active");
|
||||
}
|
||||
}
|
||||
|
||||
let active_gateway =
|
||||
get_active_gateway_identity(self.storage.gateway_details_store()).await?;
|
||||
|
||||
// Determine the gateway setup based on the currently active gateway and the user-chosen
|
||||
// gateway.
|
||||
let gateway_setup = match (self.config.user_chosen_gateway.as_ref(), active_gateway) {
|
||||
// When a user-chosen gateway exists and matches the active one.
|
||||
(Some(user_chosen_gateway), Some(active_gateway))
|
||||
if &active_gateway.to_base58_string() == user_chosen_gateway =>
|
||||
{
|
||||
GatewaySetup::MustLoad { gateway_id: None }
|
||||
}
|
||||
// When a user-chosen gateway exists but there's no active gateway, or it doesn't match the active one.
|
||||
(Some(_), _) => self.new_gateway_setup().await?,
|
||||
// When no user-chosen gateway exists but there's an active gateway.
|
||||
(None, Some(_)) => GatewaySetup::MustLoad { gateway_id: None },
|
||||
// When there's no user-chosen gateway and no active gateway.
|
||||
(None, None) => self.new_gateway_setup().await?,
|
||||
};
|
||||
|
||||
// this will perform necessary key and details load and optional store
|
||||
let _init_result = nym_client_core::init::setup_gateway(
|
||||
let init_results = setup_gateway(
|
||||
gateway_setup,
|
||||
self.storage.key_store(),
|
||||
self.storage.gateway_details_store(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
set_active_gateway(
|
||||
self.storage.gateway_details_store(),
|
||||
&init_results.gateway_id().to_base58_string(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.state = BuilderState::Registered {};
|
||||
Ok(())
|
||||
}
|
||||
@@ -527,13 +550,8 @@ where
|
||||
}
|
||||
|
||||
async fn connect_to_mixnet_common(mut self) -> Result<(BaseClient, Recipient)> {
|
||||
// if we don't care about our keys, explicitly register
|
||||
if !self.config.key_mode.is_keep() {
|
||||
self.register_and_authenticate_gateway().await?;
|
||||
}
|
||||
|
||||
// otherwise, the whole key setup and gateway selection dance will be done for us
|
||||
// when we start the base client
|
||||
self.setup_client_keys().await?;
|
||||
self.setup_gateway().await?;
|
||||
|
||||
let nyxd_endpoints = self.get_nyxd_endpoints();
|
||||
let nym_api_endpoints = self.get_api_endpoints();
|
||||
@@ -543,25 +561,11 @@ where
|
||||
.config
|
||||
.as_base_client_config(nyxd_endpoints, nym_api_endpoints.clone());
|
||||
|
||||
let known_gateway = self.has_active_gateway().await;
|
||||
|
||||
// if we have a known gateway, don't bother doing all of those queries
|
||||
let gateway_setup = if known_gateway {
|
||||
None
|
||||
} else {
|
||||
Some(self.new_gateway_setup().await?)
|
||||
};
|
||||
|
||||
let mut base_builder: BaseClientBuilder<_, _> =
|
||||
BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client)
|
||||
.with_wait_for_gateway(self.wait_for_gateway)
|
||||
.with_wireguard_connection(self.wireguard_mode);
|
||||
|
||||
if !known_gateway {
|
||||
// safety: `gateway_setup` is always set whenever `known_gateway` is false
|
||||
base_builder = base_builder.with_gateway_setup(gateway_setup.unwrap());
|
||||
}
|
||||
|
||||
// let mut base_builder: BaseClientBuilder<_, _> = if !known_gateway {
|
||||
// // we need to setup a new gateway
|
||||
// let setup = self.new_gateway_setup().await;
|
||||
|
||||
@@ -5,30 +5,12 @@ use url::Url;
|
||||
|
||||
const DEFAULT_SDK_CLIENT_ID: &str = "_default-nym-sdk-client";
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub enum KeyMode {
|
||||
/// Use existing key files if they exists, otherwise create new ones.
|
||||
#[default]
|
||||
Keep,
|
||||
/// Create new keys, overwriting any potential previously existing keys.
|
||||
Overwrite,
|
||||
}
|
||||
|
||||
impl KeyMode {
|
||||
pub(crate) fn is_keep(&self) -> bool {
|
||||
matches!(self, KeyMode::Keep)
|
||||
}
|
||||
}
|
||||
|
||||
/// Config struct for [`crate::mixnet::MixnetClient`]
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
/// If the user has explicitly specified a gateway.
|
||||
pub user_chosen_gateway: Option<String>,
|
||||
|
||||
/// Determines how to handle existing key files found.
|
||||
pub key_mode: KeyMode,
|
||||
|
||||
/// The details of the network we're using. It defaults to the mainnet network.
|
||||
pub network_details: NymNetworkDetails,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user