Compare commits

...

11 Commits

Author SHA1 Message Date
Jon Häggblad cb929b251e Rename to set_active_gateway_if_previously_registered 2024-04-14 20:35:45 +02:00
Jon Häggblad b927acb35a Revert set_active_gateway in base_client 2024-04-14 20:35:45 +02:00
Jon Häggblad 9131a551d5 Downgrade log statement to debug 2024-04-14 20:35:45 +02:00
Jon Häggblad 41aa391d9d Make create_bandwidth_client pub 2024-04-14 20:35:45 +02:00
Jon Häggblad f44e17489d Remove pseudo builder methods 2024-04-14 20:35:45 +02:00
Jon Häggblad e3f893f4c4 Remove pub from internal setters 2024-04-14 20:35:45 +02:00
Jon Häggblad 2dffa07be1 Remove unused KeyMode type 2024-04-14 20:35:45 +02:00
Jon Häggblad 69e8b12328 Rework gateway setup in rust sdk mixnet client 2024-04-14 20:35:45 +02:00
Jon Häggblad 46b59cdb09 Add debug implementations for some gateway setup types 2024-04-14 20:35:44 +02:00
Jon Häggblad ad860303e8 Elevate log statements in gateway setup 2024-04-14 20:31:55 +02:00
Jon Häggblad 16e1a154e0 Set active gateway after setting up gateway in base client 2024-04-14 20:31:55 +02:00
9 changed files with 141 additions and 135 deletions
+1
View File
@@ -18,6 +18,7 @@ pub mod acquire;
pub mod error;
mod utils;
#[derive(Debug)]
pub struct BandwidthController<C, St> {
storage: St,
client: C,
+3 -3
View File
@@ -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;
+7
View File
@@ -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;
+1 -1
View File
@@ -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::{
+117 -113
View File
@@ -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;
-18
View File
@@ -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,