Save to JSON in addition to printing (#1864)

* Save to JSON in addition to printing

* Save node details to json for mixnode

* Remove Cargo.locks

* Cli ergonomics

* Json output for gateway
This commit is contained in:
Drazen Urch
2023-01-09 12:51:21 +01:00
committed by GitHub
parent 88d813b9c1
commit 4a8a9096dd
22 changed files with 237 additions and 14536 deletions
+3 -1
View File
@@ -38,4 +38,6 @@ validator-config
validator-api-config.toml
dist
storybook-static
envs/qwerty.env
envs/qwerty.env
Cargo.lock
nym-connect/Cargo.lock
Generated
-7131
View File
File diff suppressed because it is too large Load Diff
@@ -27,6 +27,7 @@ nymsphinx = { path = "../../nymsphinx" }
pemstore = { path = "../../pemstore" }
validator-client = { path = "../validator-client", optional = true }
task = { path = "../../task" }
serde = { version = "1.0", features = ["derive"]}
[dependencies.tungstenite]
+1 -1
View File
@@ -26,5 +26,5 @@ nymsphinx-framing = { path = "../nymsphinx/framing" }
nymsphinx-params = { path = "../nymsphinx/params" }
nymsphinx-types = { path = "../nymsphinx/types" }
task = { path = "../task" }
validator-client = { path = "../client-libs/validator-client" }
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"]}
version-checker = { path = "../version-checker" }
+2 -1
View File
@@ -9,7 +9,8 @@ use futures::StreamExt;
use log::*;
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::net::{SocketAddr, ToSocketAddrs};
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::Arc;
use std::time::Duration;
use task::TaskClient;
+35
View File
@@ -5,6 +5,7 @@ use mixnet_contract_common::{
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -75,3 +76,37 @@ impl GatewayBond {
})
}
}
#[derive(Serialize, Deserialize)]
pub struct GatewayNodeDetailsResponse {
pub identity_key: String,
pub sphinx_key: String,
pub owner_signature: String,
pub announce_address: String,
pub bind_address: String,
pub version: String,
pub mix_port: u16,
pub clients_port: u16,
pub data_store: String,
}
impl fmt::Display for GatewayNodeDetailsResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Identity Key: {}", self.identity_key)?;
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
writeln!(
f,
"Host: {} (bind address: {})",
self.announce_address, self.bind_address
)?;
writeln!(f, "Version: {}", self.version)?;
writeln!(
f,
"Mix Port: {}, Clients port: {}",
self.mix_port, self.clients_port
)?;
writeln!(f, "Data store is at: {}", self.data_store)
}
}
+40
View File
@@ -12,6 +12,7 @@ use mixnet_contract_common::{
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -161,3 +162,42 @@ impl MixNodeCostParams {
})
}
}
#[derive(Serialize, Deserialize)]
pub struct MixnodeNodeDetailsResponse {
pub identity_key: String,
pub sphinx_key: String,
pub owner_signature: String,
pub announce_address: String,
pub bind_address: String,
pub version: String,
pub mix_port: u16,
pub http_api_port: u16,
pub verloc_port: u16,
pub wallet_address: Option<String>,
}
impl fmt::Display for MixnodeNodeDetailsResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let wallet_address = self.wallet_address.clone().unwrap_or_default();
writeln!(f, "Identity Key: {}", self.identity_key)?;
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
writeln!(
f,
"Host: {} (bind address: {})",
self.announce_address, self.bind_address
)?;
writeln!(f, "Version: {}", self.version)?;
writeln!(
f,
"Mix Port: {}, Verloc port: {}, Http Port: {}\n",
self.mix_port, self.verloc_port, self.http_api_port
)?;
writeln!(
f,
"You are bonding to wallet address: {}\n\n",
wallet_address
)
}
}
+6 -3
View File
@@ -31,7 +31,7 @@ log = "0.4"
once_cell = "1.7.2"
pretty_env_logger = "0.4"
rand = "0.7"
serde = { version = "1.0.104", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.5", features = [
"runtime-tokio-rustls",
"sqlite",
@@ -58,7 +58,7 @@ credentials = { path = "../common/credentials" }
config = { path = "../common/config" }
crypto = { path = "../common/crypto" }
completions = { path = "../common/completions" }
logging = { path = "../common/logging"}
logging = { path = "../common/logging" }
gateway-requests = { path = "gateway-requests" }
mixnet-client = { path = "../common/client-libs/mixnet-client" }
mixnode-common = { path = "../common/mixnode-common" }
@@ -71,6 +71,9 @@ validator-client = { path = "../common/client-libs/validator-client", features =
"nyxd-client",
] }
version-checker = { path = "../common/version-checker" }
nym-types = { path = "../common/types" }
serde_json = "1"
atty = "0.2"
[features]
coconut = [
@@ -87,4 +90,4 @@ sqlx = { version = "0.5", features = [
"sqlite",
"macros",
"migrate",
] }
] }
+3 -2
View File
@@ -4,6 +4,7 @@
use crate::{
commands::{override_config, OverrideConfig},
config::{persistence::pathfinder::GatewayPathfinder, Config},
OutputFormat,
};
use clap::Args;
use config::NymConfig;
@@ -101,7 +102,7 @@ impl From<Init> for OverrideConfig {
}
}
pub async fn execute(args: &Init) {
pub async fn execute(args: &Init, output: OutputFormat) {
println!("Initialising gateway {}...", args.id);
let already_init = if Config::default_config_file_path(Some(&args.id)).exists() {
@@ -157,7 +158,7 @@ pub async fn execute(args: &Init) {
crate::node::create_gateway(config)
.await
.print_node_details();
.print_node_details(output);
}
#[cfg(test)]
+5 -3
View File
@@ -66,10 +66,12 @@ pub(crate) struct OverrideConfig {
pub(crate) async fn execute(args: Cli) {
let bin_name = "nym-gateway";
let output = args.output();
match &args.command {
Commands::Init(m) => init::execute(m).await,
Commands::NodeDetails(m) => node_details::execute(m).await,
Commands::Run(m) => run::execute(m).await,
Commands::Init(m) => init::execute(m, output.clone()).await,
Commands::NodeDetails(m) => node_details::execute(m, output.clone()).await,
Commands::Run(m) => run::execute(m, output.clone()).await,
Commands::Sign(m) => sign::execute(m),
Commands::Upgrade(m) => upgrade::execute(m).await,
Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name),
+3 -3
View File
@@ -1,7 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::{config::Config, OutputFormat};
use clap::Args;
use config::NymConfig;
use log::error;
@@ -13,7 +13,7 @@ pub struct NodeDetails {
id: String,
}
pub async fn execute(args: &NodeDetails) {
pub async fn execute(args: &NodeDetails, output: OutputFormat) {
let config = match Config::load_from_file(Some(&args.id)) {
Ok(cfg) => cfg,
Err(err) => {
@@ -28,5 +28,5 @@ pub async fn execute(args: &NodeDetails) {
crate::node::create_gateway(config)
.await
.print_node_details();
.print_node_details(output);
}
+3 -2
View File
@@ -4,6 +4,7 @@
use crate::{
commands::{override_config, version_check, OverrideConfig},
config::Config,
OutputFormat,
};
use clap::Args;
use config::NymConfig;
@@ -116,7 +117,7 @@ fn special_addresses() -> Vec<&'static str> {
vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]
}
pub async fn execute(args: &Run) {
pub async fn execute(args: &Run, output: OutputFormat) {
println!("Starting gateway {}...", args.id);
let mut config = match Config::load_from_file(Some(&args.id)) {
@@ -146,7 +147,7 @@ pub async fn execute(args: &Run) {
println!(
"\nTo bond your gateway you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\
Select the correct version and install it to your machine. You will need to provide the following: \n ");
gateway.print_node_details();
gateway.print_node_details(output);
gateway.run().await;
}
+29 -2
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use build_information::BinaryBuildInformation;
use clap::{crate_version, Parser};
use clap::{crate_version, Parser, ValueEnum};
use lazy_static::lazy_static;
use logging::setup_logging;
use network_defaults::setup_env;
@@ -21,6 +21,18 @@ fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
}
#[derive(Clone, ValueEnum)]
pub enum OutputFormat {
Json,
Text,
}
impl Default for OutputFormat {
fn default() -> Self {
OutputFormat::Text
}
}
#[derive(Parser)]
#[clap(author = "Nymtech", version, about, long_version = pretty_build_info_static())]
struct Cli {
@@ -28,14 +40,29 @@ struct Cli {
#[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
#[clap(short, long)]
pub(crate) output: Option<OutputFormat>,
#[clap(subcommand)]
command: commands::Commands,
}
impl Cli {
fn output(&self) -> OutputFormat {
if let Some(ref output) = self.output {
output.clone()
} else {
OutputFormat::default()
}
}
}
#[tokio::main]
async fn main() {
setup_logging();
println!("{}", banner());
if atty::is(atty::Stream::Stdout) {
println!("{}", banner());
}
let args = Cli::parse();
setup_env(args.config_env_file.as_ref());
+26 -26
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use self::storage::PersistentStorage;
use crate::commands::sign::load_identity_keys;
use crate::commands::validate_bech32_address_or_exit;
use crate::config::persistence::pathfinder::GatewayPathfinder;
use crate::config::Config;
@@ -11,6 +10,7 @@ use crate::node::client_handling::websocket;
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
use crate::node::statistics::collector::GatewayStatisticsCollector;
use crate::node::storage::Storage;
use crate::{commands::sign::load_identity_keys, OutputFormat};
use colored::Colorize;
use crypto::asymmetric::{encryption, identity};
use log::*;
@@ -129,32 +129,32 @@ where
verification_code
}
pub(crate) fn print_node_details(&self) {
println!(
"Identity Key: {}",
self.identity_keypair.public_key().to_base58_string()
);
println!(
"Sphinx Key: {}",
self.sphinx_keypair.public_key().to_base58_string()
);
println!("Owner Signature: {}", self.generate_owner_signature());
println!(
"Host: {} (bind address: {})",
self.config.get_announce_address(),
self.config.get_listening_address()
);
println!("Version: {}", self.config.get_version());
println!(
"Mix Port: {}, Clients port: {}",
self.config.get_mix_port(),
self.config.get_clients_port()
);
pub(crate) fn print_node_details(&self, output: OutputFormat) {
let node_details = nym_types::gateway::GatewayNodeDetailsResponse {
identity_key: self.identity_keypair.public_key().to_base58_string(),
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
owner_signature: self.generate_owner_signature(),
announce_address: self.config.get_announce_address(),
bind_address: self.config.get_listening_address().to_string(),
version: self.config.get_version().to_string(),
mix_port: self.config.get_mix_port(),
clients_port: self.config.get_clients_port(),
data_store: self
.config
.get_persistent_store_path()
.to_str()
.unwrap_or(".")
.to_string(),
};
println!(
"Data store is at: {:?}",
self.config.get_persistent_store_path()
);
match output {
OutputFormat::Json => println!(
"{}",
serde_json::to_string(&node_details)
.unwrap_or_else(|_| "Could not serialize node details".to_string())
),
OutputFormat::Text => println!("{}", node_details),
}
}
fn start_mix_socket_listener(
+3
View File
@@ -31,11 +31,13 @@ pretty_env_logger = "0.4.0"
rand = "0.7.3"
rocket = { version = "0.5.0-rc.2", features = ["json"] }
serde = { version="1.0", features = ["derive"] }
serde_json = "1.0"
sysinfo = "0.24.1"
tokio = { version="1.21.2", features = ["rt-multi-thread", "net", "signal"] }
tokio-util = { version="0.7.3", features = ["codec"] }
toml = "0.5.8"
url = { version = "2.2", features = ["serde"] }
atty = "0.2"
## internal
build-information = { path = "../common/build-information" }
@@ -49,6 +51,7 @@ nonexhaustive-delayqueue = { path="../common/nonexhaustive-delayqueue" }
nymsphinx = { path="../common/nymsphinx" }
pemstore = { path="../common/pemstore" }
task = { path = "../common/task" }
nym-types = { path = "../common/types" }
topology = { path="../common/topology" }
validator-client = { path="../common/client-libs/validator-client" }
version-checker = { path="../common/version-checker" }
+8 -8
View File
@@ -4,6 +4,7 @@
use super::OverrideConfig;
use crate::config::Config;
use crate::node::MixNode;
use crate::OutputFormat;
use crate::{commands::override_config, config::persistence::pathfinder::MixNodePathfinder};
use clap::Args;
use config::NymConfig;
@@ -62,13 +63,13 @@ impl From<Init> for OverrideConfig {
}
}
pub(crate) fn execute(args: &Init) {
pub(crate) fn execute(args: &Init, output: OutputFormat) {
let override_config_fields = OverrideConfig::from(args.clone());
let id = &override_config_fields.id;
println!("Initialising mixnode {}...", id);
eprintln!("Initialising mixnode {}...", id);
let already_init = if Config::default_config_file_path(Some(id)).exists() {
println!("Mixnode \"{}\" was already initialised before! Config information will be overwritten (but keys will be kept)!", id);
eprintln!("Mixnode \"{}\" was already initialised before! Config information will be overwritten (but keys will be kept)!", id);
true
} else {
false
@@ -101,16 +102,15 @@ pub(crate) fn execute(args: &Init) {
),
)
.expect("Failed to save sphinx keys");
println!("Saved mixnet identity and sphinx keypairs");
eprintln!("Saved mixnet identity and sphinx keypairs");
}
let config_save_location = config.get_config_file_save_location();
config
.save_to_file(None)
.expect("Failed to save the config file");
println!("Saved configuration file to {:?}", config_save_location);
println!("Mixnode configuration completed.\n\n\n");
eprintln!("Saved configuration file to {:?}", config_save_location);
eprintln!("Mixnode configuration completed.\n\n\n");
MixNode::new(config).print_node_details()
MixNode::new(config).print_node_details(output)
}
+9 -7
View File
@@ -62,13 +62,15 @@ struct OverrideConfig {
pub(crate) async fn execute(args: Cli) {
let bin_name = "nym-mixnode";
let output = args.output();
match args.command {
Commands::Describe(m) => describe::execute(m),
Commands::Init(m) => init::execute(&m),
Commands::Run(m) => run::execute(&m).await,
Commands::Init(m) => init::execute(&m, output),
Commands::Run(m) => run::execute(&m, output).await,
Commands::Sign(m) => sign::execute(&m),
Commands::Upgrade(m) => upgrade::execute(&m),
Commands::NodeDetails(m) => node_details::execute(&m),
Commands::NodeDetails(m) => node_details::execute(&m, output),
Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name),
}
@@ -115,8 +117,8 @@ pub(crate) fn validate_bech32_address_or_exit(address: &str) {
bech32_address_validation::try_bech32_decode(address)
{
let error_message = format!("Error: wallet address decoding failed: {err}").red();
println!("{}", error_message);
println!("Exiting...");
error!("{}", error_message);
error!("Exiting...");
process::exit(1);
}
@@ -124,8 +126,8 @@ pub(crate) fn validate_bech32_address_or_exit(address: &str) {
bech32_address_validation::validate_bech32_prefix(&prefix, address)
{
let error_message = format!("Error: wallet address type is wrong, {err}").red();
println!("{}", error_message);
println!("Exiting...");
error!("{}", error_message);
error!("Exiting...");
process::exit(1);
}
}
+3 -2
View File
@@ -3,6 +3,7 @@
use crate::config::Config;
use crate::node::MixNode;
use crate::OutputFormat;
use clap::Args;
use config::NymConfig;
@@ -13,7 +14,7 @@ pub(crate) struct NodeDetails {
id: String,
}
pub(crate) fn execute(args: &NodeDetails) {
pub(crate) fn execute(args: &NodeDetails, output: OutputFormat) {
let config = match Config::load_from_file(Some(&args.id)) {
Ok(cfg) => cfg,
Err(err) => {
@@ -26,5 +27,5 @@ pub(crate) fn execute(args: &NodeDetails) {
}
};
MixNode::new(config).print_node_details()
MixNode::new(config).print_node_details(output)
}
+5 -4
View File
@@ -5,6 +5,7 @@ use super::OverrideConfig;
use crate::commands::{override_config, version_check};
use crate::config::Config;
use crate::node::MixNode;
use crate::OutputFormat;
use clap::Args;
use config::NymConfig;
use std::net::IpAddr;
@@ -76,8 +77,8 @@ fn special_addresses() -> Vec<&'static str> {
vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]
}
pub(crate) async fn execute(args: &Run) {
println!("Starting mixnode {}...", args.id);
pub(crate) async fn execute(args: &Run, output: OutputFormat) {
eprintln!("Starting mixnode {}...", args.id);
let mut config = match Config::load_from_file(Some(&args.id)) {
Ok(cfg) => cfg,
@@ -104,10 +105,10 @@ pub(crate) async fn execute(args: &Run) {
let mut mixnode = MixNode::new(config);
println!(
eprintln!(
"\nTo bond your mixnode you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\
Select the correct version and install it to your machine. You will need to provide the following: \n ");
mixnode.print_node_details();
mixnode.print_node_details(output);
mixnode.run().await
}
+29 -2
View File
@@ -6,7 +6,7 @@ extern crate rocket;
use ::config::defaults::setup_env;
use build_information::BinaryBuildInformation;
use clap::{crate_version, Parser};
use clap::{crate_version, Parser, ValueEnum};
use lazy_static::lazy_static;
use logging::setup_logging;
@@ -24,6 +24,18 @@ fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
}
#[derive(Clone, ValueEnum)]
enum OutputFormat {
Json,
Text,
}
impl Default for OutputFormat {
fn default() -> Self {
OutputFormat::Text
}
}
#[derive(Parser)]
#[clap(author = "Nymtech", version, about, long_version = pretty_build_info_static())]
struct Cli {
@@ -31,14 +43,29 @@ struct Cli {
#[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
#[clap(short, long)]
pub(crate) output: Option<OutputFormat>,
#[clap(subcommand)]
command: commands::Commands,
}
impl Cli {
fn output(&self) -> OutputFormat {
if let Some(ref output) = self.output {
output.clone()
} else {
OutputFormat::default()
}
}
}
#[tokio::main]
async fn main() {
setup_logging();
println!("{}", banner());
if atty::is(atty::Stream::Stdout) {
println!("{}", banner());
}
let args = Cli::parse();
setup_env(args.config_env_file.as_ref());
+23 -29
View File
@@ -17,6 +17,7 @@ use crate::node::listener::Listener;
use crate::node::node_description::NodeDescription;
use crate::node::node_statistics::SharedNodeStats;
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
use crate::OutputFormat;
use ::crypto::asymmetric::{encryption, identity};
use colored::Colorize;
use config::NymConfig;
@@ -101,35 +102,28 @@ impl MixNode {
}
/// Prints relevant node details to the console
pub(crate) fn print_node_details(&self) {
println!(
"Identity Key: {}",
self.identity_keypair.public_key().to_base58_string()
);
println!(
"Sphinx Key: {}",
self.sphinx_keypair.public_key().to_base58_string()
);
println!("Owner Signature: {}", self.generate_owner_signature());
println!(
"Host: {} (bind address: {})",
self.config.get_announce_address(),
self.config.get_listening_address()
);
println!("Version: {}", self.config.get_version());
println!(
"Mix Port: {}, Verloc port: {}, Http Port: {}\n",
self.config.get_mix_port(),
self.config.get_verloc_port(),
self.config.get_http_api_port()
);
println!(
"You are bonding to wallet address: {}\n\n",
self.config
.get_wallet_address()
.map(|addr| addr.to_string())
.unwrap_or_else(|| "UNSPECIFIED".to_string())
);
pub(crate) fn print_node_details(&self, output: OutputFormat) {
let node_details = nym_types::mixnode::MixnodeNodeDetailsResponse {
identity_key: self.identity_keypair.public_key().to_base58_string(),
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
owner_signature: self.generate_owner_signature(),
announce_address: self.config.get_announce_address(),
bind_address: self.config.get_listening_address().to_string(),
version: self.config.get_version().to_string(),
mix_port: self.config.get_mix_port(),
http_api_port: self.config.get_http_api_port(),
verloc_port: self.config.get_verloc_port(),
wallet_address: self.config.get_wallet_address().map(|x| x.to_string()),
};
match output {
OutputFormat::Json => println!(
"{}",
serde_json::to_string(&node_details)
.unwrap_or_else(|_| "Could not serialize node details".to_string())
),
OutputFormat::Text => println!("{}", node_details),
}
}
fn start_http_api(
-7309
View File
File diff suppressed because it is too large Load Diff