diff --git a/clients/native/src/commands/add_gateway.rs b/clients/native/src/commands/add_gateway.rs new file mode 100644 index 0000000000..254839af04 --- /dev/null +++ b/clients/native/src/commands/add_gateway.rs @@ -0,0 +1,30 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::CliNativeClient; +use crate::error::ClientError; +use nym_bin_common::output_format::OutputFormat; +use nym_client_core::cli_helpers::client_add_gateway::{add_gateway, CommonClientAddGatewayArgs}; + +#[derive(clap::Args)] +pub(crate) struct Args { + #[command(flatten)] + common_args: CommonClientAddGatewayArgs, + + #[arg(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +impl AsRef for Args { + fn as_ref(&self) -> &CommonClientAddGatewayArgs { + &self.common_args + } +} + +pub(crate) async fn execute(args: Args) -> Result<(), ClientError> { + let output = args.output; + let res = add_gateway::(args).await?; + + println!("{}", output.format(&res)); + Ok(()) +} diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index 9c7c016580..3f8e86e94f 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -20,6 +20,7 @@ use std::error::Error; use std::net::IpAddr; use std::sync::OnceLock; +mod add_gateway; pub(crate) mod build_info; pub(crate) mod import_credential; pub(crate) mod init; @@ -77,6 +78,9 @@ pub(crate) enum Commands { /// List all registered with gateways ListGateways(list_gateways::Args), + /// Add new gateway to this client + AddGateway(add_gateway::Args), + /// Change the currently active gateway. Note that you must have already registered with the new gateway! SwitchGateway(switch_gateway::Args), @@ -110,6 +114,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box run::execute(m).await?, Commands::ImportCredential(m) => import_credential::execute(m).await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, + Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), diff --git a/clients/socks5/src/commands/add_gateway.rs b/clients/socks5/src/commands/add_gateway.rs new file mode 100644 index 0000000000..fb7b645619 --- /dev/null +++ b/clients/socks5/src/commands/add_gateway.rs @@ -0,0 +1,30 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::CliSocks5Client; +use crate::error::Socks5ClientError; +use nym_bin_common::output_format::OutputFormat; +use nym_client_core::cli_helpers::client_add_gateway::{add_gateway, CommonClientAddGatewayArgs}; + +#[derive(clap::Args)] +pub(crate) struct Args { + #[command(flatten)] + common_args: CommonClientAddGatewayArgs, + + #[arg(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +impl AsRef for Args { + fn as_ref(&self) -> &CommonClientAddGatewayArgs { + &self.common_args + } +} + +pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> { + let output = args.output; + let res = add_gateway::(args).await?; + + println!("{}", output.format(&res)); + Ok(()) +} diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index d982b86a9e..961c6551e5 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -24,6 +24,7 @@ use std::error::Error; use std::net::IpAddr; use std::sync::OnceLock; +mod add_gateway; pub(crate) mod build_info; mod import_credential; pub mod init; @@ -81,6 +82,9 @@ pub(crate) enum Commands { /// List all registered with gateways ListGateways(list_gateways::Args), + /// Add new gateway to this client + AddGateway(add_gateway::Args), + /// Change the currently active gateway. Note that you must have already registered with the new gateway! SwitchGateway(switch_gateway::Args), @@ -117,6 +121,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box run::execute(m).await?, Commands::ImportCredential(m) => import_credential::execute(m).await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, + Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), diff --git a/common/client-core/src/cli_helpers/client_add_gateway.rs b/common/client-core/src/cli_helpers/client_add_gateway.rs new file mode 100644 index 0000000000..afcb236ec7 --- /dev/null +++ b/common/client-core/src/cli_helpers/client_add_gateway.rs @@ -0,0 +1,174 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli_helpers::types::GatewayInfo; +use crate::cli_helpers::{CliClient, CliClientConfig}; +use crate::client::base_client::non_wasm_helpers::setup_fs_gateways_storage; +use crate::{ + client::{ + base_client::storage::helpers::{get_all_registered_identities, set_active_gateway}, + key_manager::persistence::OnDiskKeys, + }, + error::ClientCoreError, + init::types::{GatewaySelectionSpecification, GatewaySetup}, +}; +use log::info; +use nym_client_core_gateways_storage::GatewayDetails; +use nym_crypto::asymmetric::identity; +use nym_topology::NymTopology; +use std::path::PathBuf; + +#[cfg_attr(feature = "cli", derive(clap::Args))] +#[derive(Debug, Clone)] +pub struct CommonClientAddGatewayArgs { + /// Id of client we want to add gateway for. + #[cfg_attr(feature = "cli", clap(long))] + pub id: String, + + /// Explicitly specify id of the gateway to register with. + /// If unspecified, a random gateway will be chosen instead. + #[cfg_attr(feature = "cli", clap(long))] + pub gateway: Option, + + /// Specifies whether the client will attempt to enforce tls connection to the desired gateway. + #[cfg_attr(feature = "cli", clap(long))] + pub force_tls_gateway: bool, + + /// Specifies whether the new gateway should be determined based by latency as opposed to being chosen + /// uniformly. + #[cfg_attr(feature = "cli", clap(long, conflicts_with = "gateway"))] + pub latency_based_selection: bool, + + /// Force register gateway. WARNING: this will overwrite any existing keys for the given id, + /// potentially causing loss of access. + #[cfg_attr(feature = "cli", clap(long))] + pub force_register_gateway: bool, + + /// Specify whether this new gateway should be set as the active one + #[cfg_attr(feature = "cli", clap(long, default_value_t = true))] + pub set_active: bool, + + /// Comma separated list of rest endpoints of the API validators + #[cfg_attr( + feature = "cli", + clap( + long, + alias = "api_validators", + value_delimiter = ',', + group = "network" + ) + )] + pub nym_apis: Option>, + + /// Path to .json file containing custom network specification. + #[cfg_attr(feature = "cli", clap(long, group = "network", hide = true))] + pub custom_mixnet: Option, +} + +pub async fn add_gateway(args: A) -> Result +where + A: AsRef, + C: CliClient, +{ + let common_args = args.as_ref(); + let id = &common_args.id; + + let config = C::try_load_current_config(id).await?; + let core = config.core_config(); + let paths = config.common_paths(); + + let key_store = OnDiskKeys::new(paths.keys.clone()); + let details_store = setup_fs_gateways_storage(&paths.gateway_registrations).await?; + + let user_wants_force_register = common_args.force_register_gateway; + if user_wants_force_register { + eprintln!("Instructed to force registering gateway. This might overwrite keys!"); + } + + // Attempt to use a user-provided gateway, if possible + let user_chosen_gateway_id = common_args.gateway; + log::debug!("User chosen gateway id: {user_chosen_gateway_id:?}"); + + let selection_spec = GatewaySelectionSpecification::new( + user_chosen_gateway_id.map(|id| id.to_base58_string()), + Some(common_args.latency_based_selection), + common_args.force_tls_gateway, + ); + log::debug!("Gateway selection specification: {selection_spec:?}"); + + let registered_gateways = get_all_registered_identities(&details_store).await?; + + // if user provided gateway id (and we can't overwrite data), make sure we're not trying to register + // with a known gateway + if let Some(user_chosen) = user_chosen_gateway_id { + if !common_args.force_register_gateway && registered_gateways.contains(&user_chosen) { + return Err(ClientCoreError::AlreadyRegistered { + gateway_id: user_chosen.to_base58_string(), + } + .into()); + } + } + + // Setup gateway by either registering a new one, or creating a new config from the selected + // one but with keys kept, or reusing the gateway configuration. + let available_gateways = if let Some(custom_mixnet) = common_args.custom_mixnet.as_ref() { + let hardcoded_topology = NymTopology::new_from_file(custom_mixnet).map_err(|source| { + ClientCoreError::CustomTopologyLoadFailure { + file_path: custom_mixnet.clone(), + source, + } + })?; + hardcoded_topology.get_gateways() + } else { + let mut rng = rand::thread_rng(); + crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await? + }; + + // since we're registering with a brand new gateway, + // make sure the list of available gateways doesn't overlap the list of known gateways + let available_gateways = if common_args.force_register_gateway { + // if we're force registering, all bets are off + available_gateways + } else { + available_gateways + .into_iter() + .filter(|g| !registered_gateways.contains(g.identity())) + .collect() + }; + + let gateway_setup = GatewaySetup::New { + specification: selection_spec, + available_gateways, + overwrite_data: common_args.force_register_gateway, + wg_tun_address: None, + }; + + let init_details = + crate::init::setup_gateway(gateway_setup, &key_store, &details_store).await?; + + let address = init_details.client_address(); + + let gateway_registration = init_details.gateway_registration; + let GatewayDetails::Remote(ref gateway_details) = gateway_registration.details else { + return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?; + }; + + if common_args.set_active { + set_active_gateway( + &details_store, + &gateway_details.gateway_id.to_base58_string(), + ) + .await?; + } else { + info!("registered with new gateway {} (under address {address}), but this will not be our default address", gateway_details.gateway_id); + } + + Ok(GatewayInfo { + registration: gateway_registration.registration_timestamp, + identity: gateway_details.gateway_id, + active: common_args.set_active, + typ: gateway_registration.details.typ().to_string(), + endpoint: Some(gateway_details.gateway_listener.clone()), + wg_tun_address: gateway_details.wg_tun_address.clone(), + }) +} diff --git a/common/client-core/src/cli_helpers/client_init.rs b/common/client-core/src/cli_helpers/client_init.rs index 1d83302210..f765117d35 100644 --- a/common/client-core/src/cli_helpers/client_init.rs +++ b/common/client-core/src/cli_helpers/client_init.rs @@ -6,8 +6,7 @@ use crate::error::ClientCoreError; use crate::{ client::{ base_client::{ - non_wasm_helpers::setup_fs_gateways_storage, - storage::helpers::{get_all_registered_identities, set_active_gateway}, + non_wasm_helpers::setup_fs_gateways_storage, storage::helpers::set_active_gateway, }, key_manager::persistence::OnDiskKeys, }, @@ -52,16 +51,6 @@ pub struct CommonClientInitArgs { #[cfg_attr(feature = "cli", clap(long, conflicts_with = "gateway"))] pub latency_based_selection: bool, - /// Force register gateway. WARNING: this will overwrite any existing keys for the given id, - /// potentially causing loss of access. - #[cfg_attr(feature = "cli", clap(long))] - pub force_register_gateway: bool, - - /// If the registration is happening against new gateway, - /// specify whether it should be set as the currently active gateway - #[cfg_attr(feature = "cli", clap(long, default_value_t = true))] - pub set_active: bool, - /// Comma separated list of rest endpoints of the nyxd validators #[cfg_attr( feature = "cli", @@ -118,28 +107,16 @@ where let common_args = init_args.as_ref(); let id = &common_args.id; - let already_init = if C::default_config_path(id).exists() { - // in case we're using old config, try to upgrade it - // (if we're using the current version, it's a no-op) - C::try_upgrade_outdated_config(id).await?; + if C::default_config_path(id).exists() { eprintln!("{} client \"{id}\" was already initialised before", C::NAME); - true - } else { - info!( - "{} client {id:?} hasn't been initialised before - new keys are going to be generated", - C::NAME - ); - C::initialise_storage_paths(id)?; - false - }; - - // Usually you only register with the gateway on the first init, however you can force - // re-registering if wanted. - let user_wants_force_register = common_args.force_register_gateway; - if user_wants_force_register { - eprintln!("Instructed to force registering gateway. This might overwrite keys!"); + return Err(ClientCoreError::AlreadyInitialised { + client_id: id.to_string(), + } + .into()); } + C::initialise_storage_paths(id)?; + // Attempt to use a user-provided gateway, if possible let user_chosen_gateway_id = common_args.gateway; log::debug!("User chosen gateway id: {user_chosen_gateway_id:?}"); @@ -171,24 +148,8 @@ where let key_store = OnDiskKeys::new(paths.keys.clone()); let details_store = setup_fs_gateways_storage(&paths.gateway_registrations).await?; - // if this is a first time client with this particular id is initialised, generated long-term keys - if !already_init { - let mut rng = OsRng; - crate::init::generate_new_client_keys(&mut rng, &key_store).await?; - } - - let registered_gateways = get_all_registered_identities(&details_store).await?; - - // if user provided gateway id (and we can't overwrite data), make sure we're not trying to register - // with a known gateway - if let Some(user_chosen) = user_chosen_gateway_id { - if !common_args.force_register_gateway && registered_gateways.contains(&user_chosen) { - return Err(ClientCoreError::AlreadyRegistered { - gateway_id: user_chosen.to_base58_string(), - } - .into()); - } - } + let mut rng = OsRng; + crate::init::generate_new_client_keys(&mut rng, &key_store).await?; // Setup gateway by either registering a new one, or creating a new config from the selected // one but with keys kept, or reusing the gateway configuration. @@ -205,22 +166,10 @@ where crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await? }; - // since we're registering with a brand new gateway, - // make sure the list of available gateways doesn't overlap the list of known gateways - let available_gateways = if common_args.force_register_gateway { - // if we're force registering, all bets are off - available_gateways - } else { - available_gateways - .into_iter() - .filter(|g| !registered_gateways.contains(g.identity())) - .collect() - }; - let gateway_setup = GatewaySetup::New { specification: selection_spec, available_gateways, - overwrite_data: common_args.force_register_gateway, + overwrite_data: false, wg_tun_address: None, }; @@ -228,25 +177,22 @@ where crate::init::setup_gateway(gateway_setup, &key_store, &details_store).await?; // TODO: ask the service provider we specified for its interface version and set it in the config - - if !already_init { - let config_save_location = config.default_store_location(); - if let Err(err) = config.save_to(&config_save_location) { - return Err(ClientCoreError::ConfigSaveFailure { - typ: C::NAME.to_string(), - id: id.to_string(), - path: config_save_location, - source: err, - } - .into()); + let config_save_location = config.default_store_location(); + if let Err(err) = config.save_to(&config_save_location) { + return Err(ClientCoreError::ConfigSaveFailure { + typ: C::NAME.to_string(), + id: id.to_string(), + path: config_save_location, + source: err, } - - eprintln!( - "Saved configuration file to {}", - config_save_location.display() - ); + .into()); } + eprintln!( + "Saved configuration file to {}", + config_save_location.display() + ); + let address = init_details.client_address(); let GatewayDetails::Remote(gateway_details) = init_details.gateway_registration.details else { @@ -260,11 +206,7 @@ where init_details.gateway_registration.registration_timestamp, ); - if init_args.as_ref().set_active { - set_active_gateway(&details_store, &init_results.gateway_id).await?; - } else { - info!("registered with new gateway {} (under address {address}), but this will not be our default address", init_results.gateway_id); - } + set_active_gateway(&details_store, &init_results.gateway_id).await?; Ok(InitResultsWithConfig { config, diff --git a/common/client-core/src/cli_helpers/client_list_gateways.rs b/common/client-core/src/cli_helpers/client_list_gateways.rs index 641b29d16e..1bfcafb027 100644 --- a/common/client-core/src/cli_helpers/client_list_gateways.rs +++ b/common/client-core/src/cli_helpers/client_list_gateways.rs @@ -1,17 +1,15 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use super::types::GatewayInfo; use crate::cli_helpers::{CliClient, CliClientConfig}; use crate::client::base_client::non_wasm_helpers::setup_fs_gateways_storage; use crate::client::base_client::storage::helpers::{ get_active_gateway_identity, get_gateway_registrations, }; use nym_client_core_gateways_storage::{GatewayDetails, GatewayType}; -use nym_crypto::asymmetric::identity; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; -use time::OffsetDateTime; -use url::Url; #[cfg_attr(feature = "cli", derive(clap::Args))] #[derive(Debug, Clone)] @@ -34,38 +32,6 @@ impl Display for RegisteredGateways { } } -#[derive(Serialize, Deserialize)] -pub struct GatewayInfo { - pub registration: OffsetDateTime, - pub identity: identity::PublicKey, - pub active: bool, - - pub typ: String, - pub endpoint: Option, - pub wg_tun_address: Option, -} - -impl Display for GatewayInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - if self.active { - write!(f, "[ACTIVE] ")?; - } - write!( - f, - "{} gateway '{}' registered at: {}", - self.typ, self.identity, self.registration - )?; - if let Some(endpoint) = &self.endpoint { - write!(f, " endpoint: {endpoint}")?; - } - - if let Some(wg_tun_address) = &self.wg_tun_address { - write!(f, " wg tun address: {wg_tun_address}")?; - } - Ok(()) - } -} - pub async fn list_gateways(args: A) -> Result where A: AsRef, diff --git a/common/client-core/src/cli_helpers/mod.rs b/common/client-core/src/cli_helpers/mod.rs index e2a018cb18..5ab51b8b4c 100644 --- a/common/client-core/src/cli_helpers/mod.rs +++ b/common/client-core/src/cli_helpers/mod.rs @@ -1,12 +1,14 @@ // Copyright 2023-2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +pub mod client_add_gateway; pub mod client_import_credential; pub mod client_init; pub mod client_list_gateways; pub mod client_run; pub mod client_switch_gateway; pub mod traits; +mod types; pub use client_init::InitialisableClient; pub use traits::{CliClient, CliClientConfig}; diff --git a/common/client-core/src/cli_helpers/types.rs b/common/client-core/src/cli_helpers/types.rs new file mode 100644 index 0000000000..4a351f38ef --- /dev/null +++ b/common/client-core/src/cli_helpers/types.rs @@ -0,0 +1,40 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_crypto::asymmetric::identity; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; +use time::OffsetDateTime; +use url::Url; + +#[derive(Serialize, Deserialize)] +pub struct GatewayInfo { + pub registration: OffsetDateTime, + pub identity: identity::PublicKey, + pub active: bool, + + pub typ: String, + pub endpoint: Option, + pub wg_tun_address: Option, +} + +impl Display for GatewayInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.active { + write!(f, "[ACTIVE] ")?; + } + write!( + f, + "{} gateway '{}' registered at: {}", + self.typ, self.identity, self.registration + )?; + if let Some(endpoint) = &self.endpoint { + write!(f, " endpoint: {endpoint}")?; + } + + if let Some(wg_tun_address) = &self.wg_tun_address { + write!(f, " wg tun address: {wg_tun_address}")?; + } + Ok(()) + } +} diff --git a/common/client-core/src/error.rs b/common/client-core/src/error.rs index d2d391f102..67382c468e 100644 --- a/common/client-core/src/error.rs +++ b/common/client-core/src/error.rs @@ -198,6 +198,9 @@ pub enum ClientCoreError { source: url::ParseError, }, + #[error("this client (id: '{client_id}') has already been initialised before. If you want to add additional gateway, use `add-gateway` command")] + AlreadyInitialised { client_id: String }, + #[error("this client has already registered with gateway {gateway_id}")] AlreadyRegistered { gateway_id: String }, } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs new file mode 100644 index 0000000000..fcef40052d --- /dev/null +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/nym_node.rs @@ -0,0 +1,13 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::IdentityKey; + +pub struct NymNode { + /// Network address of this mixnode, for example 1.1.1.1 or foo.nymnode.com + /// that will used for discovering other capabilities of this node. + pub host: String, + + /// Base58-encoded ed25519 EdDSA public key. + pub identity_key: IdentityKey, +} diff --git a/service-providers/ip-packet-router/src/cli/add_gateway.rs b/service-providers/ip-packet-router/src/cli/add_gateway.rs new file mode 100644 index 0000000000..3050233c87 --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/add_gateway.rs @@ -0,0 +1,32 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliIpPacketRouterClient; +use nym_bin_common::output_format::OutputFormat; +use nym_client_core::cli_helpers::client_list_gateways::{ + list_gateways, CommonClientListGatewaysArgs, +}; +use nym_ip_packet_router::error::IpPacketRouterError; + +#[derive(clap::Args)] +pub(crate) struct Args { + #[command(flatten)] + common_args: CommonClientListGatewaysArgs, + + #[arg(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +impl AsRef for Args { + fn as_ref(&self) -> &CommonClientListGatewaysArgs { + &self.common_args + } +} + +pub(crate) async fn execute(args: Args) -> Result<(), IpPacketRouterError> { + let output = args.output; + let res = list_gateways::(args).await?; + + println!("{}", output.format(&res)); + Ok(()) +} diff --git a/service-providers/ip-packet-router/src/cli/mod.rs b/service-providers/ip-packet-router/src/cli/mod.rs index 912c13cc7a..3de91de6be 100644 --- a/service-providers/ip-packet-router/src/cli/mod.rs +++ b/service-providers/ip-packet-router/src/cli/mod.rs @@ -9,6 +9,7 @@ use nym_ip_packet_router::config::{BaseClientConfig, Config}; use nym_ip_packet_router::error::IpPacketRouterError; use std::sync::OnceLock; +mod add_gateway; mod build_info; mod import_credential; mod init; @@ -69,6 +70,9 @@ pub(crate) enum Commands { /// List all registered with gateways ListGateways(list_gateways::Args), + /// Add new gateway to this client + AddGateway(add_gateway::Args), + /// Change the currently active gateway. Note that you must have already registered with the new gateway! SwitchGateway(switch_gateway::Args), @@ -126,6 +130,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), IpPacketRouterError> { Commands::Run(m) => run::execute(&m).await?, Commands::ImportCredential(m) => import_credential::execute(m).await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, + Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, Commands::Sign(m) => sign::execute(&m).await?, Commands::BuildInfo(m) => build_info::execute(m), diff --git a/service-providers/network-requester/src/cli/add_gateway.rs b/service-providers/network-requester/src/cli/add_gateway.rs new file mode 100644 index 0000000000..ec4703045b --- /dev/null +++ b/service-providers/network-requester/src/cli/add_gateway.rs @@ -0,0 +1,32 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::cli::CliNetworkRequesterClient; +use crate::error::NetworkRequesterError; +use nym_bin_common::output_format::OutputFormat; +use nym_client_core::cli_helpers::client_list_gateways::{ + list_gateways, CommonClientListGatewaysArgs, +}; + +#[derive(clap::Args)] +pub(crate) struct Args { + #[command(flatten)] + common_args: CommonClientListGatewaysArgs, + + #[arg(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +impl AsRef for Args { + fn as_ref(&self) -> &CommonClientListGatewaysArgs { + &self.common_args + } +} + +pub(crate) async fn execute(args: Args) -> Result<(), NetworkRequesterError> { + let output = args.output; + let res = list_gateways::(args).await?; + + println!("{}", output.format(&res)); + Ok(()) +} diff --git a/service-providers/network-requester/src/cli/mod.rs b/service-providers/network-requester/src/cli/mod.rs index 7ba2462c61..290c4b8980 100644 --- a/service-providers/network-requester/src/cli/mod.rs +++ b/service-providers/network-requester/src/cli/mod.rs @@ -16,6 +16,7 @@ use nym_client_core::cli_helpers::CliClient; use nym_config::OptionalSet; use std::sync::OnceLock; +mod add_gateway; mod build_info; mod import_credential; mod init; @@ -79,6 +80,9 @@ pub(crate) enum Commands { /// List all registered with gateways ListGateways(list_gateways::Args), + /// Add new gateway to this client + AddGateway(add_gateway::Args), + /// Change the currently active gateway. Note that you must have already registered with the new gateway! SwitchGateway(switch_gateway::Args), @@ -178,6 +182,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), NetworkRequesterError> { Commands::Sign(m) => sign::execute(&m).await?, Commands::ImportCredential(m) => import_credential::execute(m).await?, Commands::ListGateways(args) => list_gateways::execute(args).await?, + Commands::AddGateway(args) => add_gateway::execute(args).await?, Commands::SwitchGateway(args) => switch_gateway::execute(args).await?, Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name),