Chore/nym api commands (#4225)

* created run and init commands for nym-api + nasty mnemonic workaround

* removed dead code

* cargo fmt + clippy

* fixed key loader

* made announce address optional and removed the nonobvious fallback value

* clippy

* removed contract addresses from config template

* fixed conflicting arguments macro

* post-rebasing fixes: applied client macro to balance method
This commit is contained in:
Jędrzej Stuczyński
2024-01-03 10:49:10 +01:00
committed by GitHub
parent 496870b5f6
commit 78610c7e28
19 changed files with 578 additions and 1271 deletions
+1 -1
View File
@@ -73,7 +73,7 @@ where
P: AsRef<Path>,
{
let path = path.as_ref();
log::debug!("trying to save config file to {}", path.display());
log::info!("saving config file to {}", path.display());
if let Some(parent) = path.parent() {
create_dir_all(parent)?;
+7
View File
@@ -3,6 +3,7 @@
use crate::var_names;
use crate::{DenomDetails, ValidatorDetails};
use std::str::FromStr;
pub const NETWORK_NAME: &str = "mainnet";
@@ -60,6 +61,12 @@ pub fn read_var_if_not_default(var: &str) -> Option<String> {
}
}
pub fn read_parsed_var_if_not_default<T: FromStr>(var: &str) -> Option<Result<T, T::Err>> {
read_var_if_not_default(var)
.as_deref()
.map(FromStr::from_str)
}
pub fn export_to_env() {
set_var_to_default(var_names::CONFIGURED, "true");
set_var_to_default(var_names::NETWORK_NAME, NETWORK_NAME);
+15 -9
View File
@@ -3,12 +3,12 @@
use crate::coconut::error::Result;
use crate::nyxd;
use nym_coconut_dkg_common::types::EpochId;
use crate::support::nyxd::ClientInner;
use nym_coconut_dkg_common::types::{Epoch, EpochId};
use nym_coconut_interface::VerificationKey;
use nym_credentials::coconut::utils::obtain_aggregate_verification_key;
use nym_validator_client::coconut::all_coconut_api_clients;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use nym_validator_client::DirectSigningHttpRpcNyxdClient;
use std::cmp::min;
use std::collections::HashMap;
use std::ops::Deref;
@@ -40,9 +40,7 @@ impl CachedEpoch {
self.valid_until > OffsetDateTime::now_utc()
}
async fn update(&mut self, client: &DirectSigningHttpRpcNyxdClient) -> Result<()> {
let epoch = client.get_current_epoch().await?;
async fn update(&mut self, epoch: Epoch) -> Result<()> {
let now = OffsetDateTime::now_utc();
let state_end =
OffsetDateTime::from_unix_timestamp(epoch.finish_timestamp.seconds() as i64).unwrap();
@@ -84,8 +82,13 @@ impl APICommunicationChannel for QueryCommunicationChannel {
// update cache
drop(guard);
let mut guard = self.cached_epoch.write().await;
let client = self.nyxd_client.0.read().await;
guard.update(&client).await?;
let epoch = match self.nyxd_client.read().await.deref() {
ClientInner::Query(client) => client.get_current_epoch().await?,
ClientInner::Signing(client) => client.get_current_epoch().await?,
};
guard.update(epoch).await?;
return Ok(guard.current_epoch_id);
}
@@ -96,8 +99,11 @@ impl APICommunicationChannel for QueryCommunicationChannel {
}
let mut guard = self.epoch_keys.write().await;
let client = self.nyxd_client.0.read().await;
let coconut_api_clients = all_coconut_api_clients(client.deref(), epoch_id).await?;
let coconut_api_clients = match self.nyxd_client.read().await.deref() {
ClientInner::Query(client) => all_coconut_api_clients(client, epoch_id).await?,
ClientInner::Signing(client) => all_coconut_api_clients(client, epoch_id).await?,
};
let vk = obtain_aggregate_verification_key(&coconut_api_clients).await?;
guard.insert(epoch_id, vk.clone());
+6 -2
View File
@@ -13,7 +13,7 @@ use crate::coconut::dkg::{
use crate::coconut::keypair::KeyPair as CoconutKeyPair;
use crate::nyxd;
use crate::support::config;
use anyhow::Result;
use anyhow::{bail, Result};
use nym_coconut_dkg_common::types::EpochState;
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
use nym_task::{TaskClient, TaskManager};
@@ -53,6 +53,10 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
coconut_keypair: CoconutKeyPair,
rng: R,
) -> Result<Self> {
let Some(announce_address) = &config.announce_address else {
bail!("can't start a DKG controller without specifying an announce address!")
};
let dkg_keypair = nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
&config.storage_paths.decryption_key_path,
&config.storage_paths.public_key_with_proof_path,
@@ -76,7 +80,7 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
state: State::new(
config.storage_paths.dkg_persistent_state_path.clone(),
persistent_state,
config.announce_address.clone(),
announce_address.clone(),
dkg_keypair,
coconut_keypair,
),
+6 -17
View File
@@ -9,7 +9,7 @@ use crate::network::models::NetworkDetails;
use crate::node_describe_cache::DescribedNodes;
use crate::node_status_api::uptime_updater::HistoricalUptimeUpdater;
use crate::support::caching::cache::SharedCache;
use crate::support::cli::{self, Commands};
use crate::support::cli;
use crate::support::config::Config;
use crate::support::storage;
use crate::support::storage::NymApiStorage;
@@ -21,11 +21,11 @@ use clap::Parser;
use coconut::dkg::controller::DkgController;
use node_status_api::NodeStatusCache;
use nym_bin_common::logging::setup_logging;
use nym_config::defaults::NymNetworkDetails;
use nym_contract_cache::cache::NymContractCache;
use nym_sphinx::receiver::SphinxMessageReceiver;
use nym_task::TaskManager;
use rand::rngs::OsRng;
use std::error::Error;
use support::{http, nyxd};
mod circulating_supply_api;
@@ -46,7 +46,7 @@ struct ShutdownHandles {
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
async fn main() -> Result<(), anyhow::Error> {
println!("Starting nym api...");
cfg_if::cfg_if! {if #[cfg(feature = "console-subscriber")] {
@@ -59,24 +59,13 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
trace!("{:#?}", args);
setup_env(args.config_env_file.as_ref());
let command = args.command.unwrap_or(Commands::Run(Box::new(args.run)));
match command {
Commands::BuildInfo(m) => {
cli::build_info::execute(m);
Ok(())
}
Commands::Run(m) => cli::run::execute(*m).await,
}
args.execute().await
}
async fn start_nym_api_tasks(
config: Config,
) -> Result<ShutdownHandles, Box<dyn Error + Send + Sync>> {
async fn start_nym_api_tasks(config: Config) -> anyhow::Result<ShutdownHandles> {
let nyxd_client = nyxd::Client::new(&config);
let connected_nyxd = config.get_nyxd_url();
let nym_network_details = config.get_network_details();
let nym_network_details = NymNetworkDetails::new_from_env();
let network_details = NetworkDetails::new(connected_nyxd.to_string(), nym_network_details);
let coconut_keypair = coconut::keypair::KeyPair::new();
+38 -28
View File
@@ -47,44 +47,52 @@ impl NymContractCacheRefresher {
}
async fn get_nym_contracts_info(&self) -> Result<CachedContractsInfo> {
use crate::query_guard;
let mut updated = HashMap::new();
let client_guard = self.nyxd_client.read().await;
let mixnet = client_guard.mixnet_contract_address();
let vesting = client_guard.vesting_contract_address();
let name_service = client_guard.name_service_contract_address();
let service_provider = client_guard.service_provider_contract_address();
let coconut_bandwidth = client_guard.coconut_bandwidth_contract_address();
let coconut_dkg = client_guard.dkg_contract_address();
let group = client_guard.group_contract_address();
let multisig = client_guard.multisig_contract_address();
let mixnet = query_guard!(client_guard, mixnet_contract_address());
let vesting = query_guard!(client_guard, vesting_contract_address());
let name_service = query_guard!(client_guard, name_service_contract_address());
let service_provider = query_guard!(client_guard, service_provider_contract_address());
let coconut_bandwidth = query_guard!(client_guard, coconut_bandwidth_contract_address());
let coconut_dkg = query_guard!(client_guard, dkg_contract_address());
let group = query_guard!(client_guard, group_contract_address());
let multisig = query_guard!(client_guard, multisig_contract_address());
// get cw2 versions
let mixnet_cw2_future = client_guard.get_mixnet_contract_cw2_version();
let vesting_cw2_future = client_guard.get_vesting_contract_cw2_version();
let service_provider_cw2_future = client_guard.get_name_service_contract_cw2_version();
let name_service_cw2_future = client_guard.get_name_service_contract_cw2_version();
let mixnet_cw2_future = query_guard!(client_guard, get_mixnet_contract_cw2_version());
let vesting_cw2_future = query_guard!(client_guard, get_vesting_contract_cw2_version());
let service_provider_cw2_future =
query_guard!(client_guard, get_name_service_contract_cw2_version());
let name_service_cw2_future =
query_guard!(client_guard, get_name_service_contract_cw2_version());
// group and multisig contract save that information in their storage but don't expose it via queries
// so a temporary workaround...
let multisig_cw2 = if let Some(multisig_contract) = multisig {
client_guard
.query_contract_raw(multisig_contract, b"contract_info".to_vec())
.await
.map(|r| serde_json::from_slice(&r).ok())
.ok()
.flatten()
query_guard!(
client_guard,
query_contract_raw(multisig_contract, b"contract_info".to_vec())
.await
.map(|r| serde_json::from_slice(&r).ok())
.ok()
.flatten()
)
} else {
None
};
let group_cw2 = if let Some(group_contract) = group {
client_guard
.query_contract_raw(group_contract, b"contract_info".to_vec())
.await
.map(|r| serde_json::from_slice(&r).ok())
.ok()
.flatten()
query_guard!(
client_guard,
query_contract_raw(group_contract, b"contract_info".to_vec())
.await
.map(|r| serde_json::from_slice(&r).ok())
.ok()
.flatten()
)
} else {
None
};
@@ -98,10 +106,12 @@ impl NymContractCacheRefresher {
.await;
// get detailed build info
let mixnet_detailed_future = client_guard.get_mixnet_contract_version();
let vesting_detailed_future = client_guard.get_vesting_contract_version();
let service_provider_detailed_future = client_guard.get_sp_contract_version();
let name_service_detailed_future = client_guard.get_name_service_contract_version();
let mixnet_detailed_future = query_guard!(client_guard, get_mixnet_contract_version());
let vesting_detailed_future = query_guard!(client_guard, get_vesting_contract_version());
let service_provider_detailed_future =
query_guard!(client_guard, get_sp_contract_version());
let name_service_detailed_future =
query_guard!(client_guard, get_name_service_contract_version());
let mut build_info = join_all(vec![
mixnet_detailed_future,
+3 -2
View File
@@ -10,6 +10,7 @@ pub(crate) struct Args {
output: OutputFormat,
}
pub(crate) fn execute(args: Args) {
println!("{}", args.output.format(&bin_info_owned!()))
pub(crate) fn execute(args: Args) -> anyhow::Result<()> {
println!("{}", args.output.format(&bin_info_owned!()));
Ok(())
}
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::support::config::default_config_filepath;
use crate::support::config::helpers::initialise_new;
use anyhow::bail;
#[derive(clap::Args, Debug)]
pub(crate) struct Args {
/// Id of the nym-api we want to initialise. if unspecified, a default value will be used.
/// default: "default"
#[clap(long, default_value = "default")]
pub(crate) id: String,
/// Specifies whether network monitoring is enabled on this API
/// default: false
#[clap(short = 'm', long)]
pub(crate) enable_monitor: bool,
/// Specifies whether network rewarding is enabled on this API
/// default: false
#[clap(short = 'r', long, requires = "enable_monitor", requires = "mnemonic")]
pub(crate) enable_rewarding: bool,
/// Endpoint to nyxd instance used for contract information.
/// default: http://localhost:26657
#[clap(long)]
pub(crate) nyxd_validator: Option<url::Url>,
/// Mnemonic of the network monitor used for sending rewarding and zk-nyms transactions
/// default: None
#[clap(long)]
pub(crate) mnemonic: Option<bip39::Mnemonic>,
/// Flag to indicate whether credential signer authority is enabled on this API
/// default: false
#[clap(
long,
requires = "mnemonic",
requires = "announce_address",
alias = "enable_coconut"
)]
pub(crate) enable_zk_nym: bool,
/// Announced address that is going to be put in the DKG contract where zk-nym clients will connect
/// to obtain their credentials
/// default: None
#[clap(long)]
pub(crate) announce_address: Option<url::Url>,
/// Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
#[clap(long, requires = "enable_monitor")]
pub(crate) monitor_credentials_mode: bool,
// #[clap(short, long, default_value_t = OutputFormat::default())]
// output: OutputFormat,
}
pub(crate) async fn execute(args: Args) -> anyhow::Result<()> {
eprintln!("initialising nym-api...");
// let output = args.output;
let config_path = default_config_filepath(&args.id);
if config_path.exists() {
// don't bother with attempting to override some of the data and preserving the rest of it
// if the config exists.
// this situation should never occur under normal circumstances, so it's up to the user to deal with it
bail!("there already exists a configuration file at '{}'. If you intend to replace it, you need to manually remove it first. Make sure to make backup of any keys and datastores first.", config_path.display())
}
let config = initialise_new(&args.id)?;
// args take precedence over env
config
.override_with_env()
.override_with_args(args)
.try_save()?;
Ok(())
}
+19 -110
View File
@@ -1,31 +1,22 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use super::config::Config;
use crate::support::config::default_config_filepath;
use crate::support::config::helpers::{initialise_new, try_load_current_config};
use ::nym_config::defaults::var_names::{MIXNET_CONTRACT_ADDRESS, VESTING_CONTRACT_ADDRESS};
use anyhow::{bail, Result};
use anyhow::Result;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use nym_bin_common::bin_info;
use nym_config::defaults::var_names::NYXD;
use nym_config::OptionalSet;
use std::sync::OnceLock;
pub(crate) mod build_info;
pub(crate) mod init;
pub(crate) mod run;
lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print();
}
// Helper for passing LONG_VERSION to clap
fn pretty_build_info_static() -> &'static str {
&PRETTY_BUILD_INFORMATION
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print())
}
#[derive(Parser, Debug)]
#[command(args_conflicts_with_subcommands = true)]
#[clap(author = "Nymtech", version, long_version = pretty_build_info_static(), about)]
pub(crate) struct Cli {
/// Path pointing to an env file that configures the Nym API.
@@ -33,109 +24,27 @@ pub(crate) struct Cli {
pub(crate) config_env_file: Option<std::path::PathBuf>,
#[clap(subcommand)]
pub(crate) command: Option<Commands>,
pub(crate) command: Commands,
}
// this shouldn't really be here, but we don't want to break backwards compat
#[clap(flatten)]
pub(crate) run: run::Args,
impl Cli {
pub(crate) async fn execute(self) -> Result<(), anyhow::Error> {
match self.command {
Commands::Init(args) => init::execute(args).await,
Commands::Run(args) => run::execute(args).await,
Commands::BuildInfo(args) => build_info::execute(args),
}
}
}
#[derive(Subcommand, Debug)]
pub(crate) enum Commands {
/// Initialise a Nym Api instance with persistent config.toml file.
Init(init::Args),
/// Run the Nym Api with provided configuration optionally overriding set parameters
Run(Box<run::Args>),
Run(run::Args),
/// Show build information of this binary
BuildInfo(build_info::Args),
}
pub(crate) fn override_config(config: Config, args: run::Args) -> Config {
config
.with_optional_env(
Config::with_custom_nyxd_validator,
args.nyxd_validator,
NYXD,
)
.with_optional_env(
Config::with_custom_mixnet_contract,
args.mixnet_contract,
MIXNET_CONTRACT_ADDRESS,
)
.with_optional_env(
Config::with_custom_vesting_contract,
args.vesting_contract,
VESTING_CONTRACT_ADDRESS,
)
.with_optional(Config::with_mnemonic, args.mnemonic)
.with_optional(
Config::with_minimum_interval_monitor_threshold,
args.monitor_threshold,
)
.with_optional(
Config::with_min_mixnode_reliability,
args.min_mixnode_reliability,
)
.with_optional(
Config::with_min_gateway_reliability,
args.min_gateway_reliability,
)
.with_optional(Config::with_network_monitor_enabled, args.enable_monitor)
.with_optional(Config::with_rewarding_enabled, args.enable_rewarding)
.with_optional(Config::with_ephemera_enabled, args.enable_ephemera)
.with_optional(
Config::with_disabled_credentials_mode,
args.enabled_credentials_mode.map(|b| !b),
)
.with_optional(Config::with_announce_address, args.announce_address)
.with_optional(Config::with_coconut_signer_enabled, args.enable_coconut)
.with_optional(Config::with_ephemera_ip, args.ephemera_args.ephemera_ip)
.with_optional(
Config::with_ephemera_protocol_port,
args.ephemera_args.ephemera_protocol_port,
)
.with_optional(
Config::with_ephemera_websocket_port,
args.ephemera_args.ephemera_websocket_port,
)
.with_optional(
Config::with_ephemera_http_api_port,
args.ephemera_args.ephemera_http_api_port,
)
}
pub(crate) fn build_config(args: run::Args) -> Result<Config> {
let id = match &args.id {
Some(id) => id.clone(),
None => {
error!("--id argument must be provided to run nym-api");
bail!("--id argument must be provided to run nym-api")
}
};
// try to load config from the file, if it doesn't exist, use default values
let config = match try_load_current_config(&id) {
Ok(cfg) => cfg,
Err(err) => {
let config_path = default_config_filepath(&id);
warn!(
"Could not load the configuration file from {}: {err}. Either the file did not exist or was malformed. Using the default values instead",
config_path.display()
);
initialise_new(&id)?
}
};
let mut config = override_config(config, args);
// since we have no proper `init`, we have to do id check here:
let made_new_keys = config
.base
.storage_paths
.generate_identity_if_missing(&config.base.id)?;
if made_new_keys {
config.save_to_default_location()?
}
Ok(config)
}
+40 -81
View File
@@ -2,114 +2,68 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::start_nym_api_tasks;
use crate::support::cli::build_config;
use nym_validator_client::nyxd;
use std::error::Error;
// explicitly defined custom parser (as opposed to just using
// #[arg(value_parser = clap::value_parser!(u8).range(0..100))]
// for better error message
fn threshold_in_range(s: &str) -> Result<u8, String> {
let threshold: usize = s
.parse()
.map_err(|_| format!("`{s}` isn't a valid threshold number"))?;
if threshold > 100 {
Err(format!("{threshold} is not within the range 0-100"))
} else {
Ok(threshold as u8)
}
}
use crate::support::config::helpers::try_load_current_config;
use anyhow::bail;
#[derive(clap::Args, Debug)]
pub(crate) struct Args {
/// Id of the nym-api we want to run
#[clap(long)]
// ugh. we had to make it optional in case somebody wanted to run `build-info`
pub(crate) id: Option<String>,
/// Id of the nym-api we want to run.if unspecified, a default value will be used.
/// default: "default"
#[clap(long, default_value = "default")]
pub(crate) id: String,
/// Specifies whether network monitoring is enabled on this API
/// default: None - config value will be used instead
#[clap(short = 'm', long)]
pub(crate) enable_monitor: Option<bool>,
/// Specifies whether network rewarding is enabled on this API
/// default: None - config value will be used instead
#[clap(short = 'r', long, requires = "enable_monitor", requires = "mnemonic")]
pub(crate) enable_rewarding: Option<bool>,
/// Specifies whether ephemera is used to aggregate monitor data on this API
#[clap(short = 'e', long, requires = "enable_monitor")]
pub(crate) enable_ephemera: Option<bool>,
/// Endpoint to nyxd instance from which the monitor will grab nodes to test
/// Endpoint to nyxd instance used for contract information.
/// default: None - config value will be used instead
#[clap(long)]
pub(crate) nyxd_validator: Option<url::Url>,
/// Address of the mixnet contract managing the network
#[clap(long)]
pub(crate) mixnet_contract: Option<nyxd::AccountId>,
/// Address of the vesting contract holding locked tokens
#[clap(long)]
pub(crate) vesting_contract: Option<nyxd::AccountId>,
/// Mnemonic of the network monitor used for rewarding operators
// even though we're currently converting the mnemonic to string (and then back to the concrete type)
// at least we're getting immediate validation when passing the arguments
/// Mnemonic of the network monitor used for sending rewarding and zk-nyms transactions
/// default: None - config value will be used instead
#[clap(long)]
pub(crate) mnemonic: Option<bip39::Mnemonic>,
/// Specifies whether a config file based on provided arguments should be saved to a file
#[clap(short = 'w', long)]
pub(crate) save_config: bool,
/// Specifies the minimum percentage of monitor test run data present in order to distribute rewards for given interval.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) monitor_threshold: Option<u8>,
/// Mixnodes with reliability lower the this get blacklisted by network monitor, get no traffic and cannot be selected into a rewarded set.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) min_mixnode_reliability: Option<u8>,
/// Gateways with reliability lower the this get blacklisted by network monitor, get no traffic and cannot be selected into a rewarded set.
#[clap(long, value_parser = threshold_in_range)]
pub(crate) min_gateway_reliability: Option<u8>,
/// Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
#[clap(long)]
pub(crate) enabled_credentials_mode: Option<bool>,
/// Announced address where coconut clients will connect.
#[clap(long, hide = true)]
pub(crate) announce_address: Option<url::Url>,
/// Flag to indicate whether coconut signer authority is enabled on this API
/// default: None - config value will be used instead
#[clap(
long,
requires = "mnemonic",
requires = "announce_address",
hide = true
alias = "enable_coconut"
)]
pub(crate) enable_coconut: Option<bool>,
pub(crate) enable_zk_nym: Option<bool>,
/// Ephemera configuration arguments.
#[command(flatten)]
pub(crate) ephemera_args: ephemera::cli::init::Cmd,
/// Announced address that is going to be put in the DKG contract where zk-nym clients will connect
/// to obtain their credentials
/// default: None - config value will be used instead
#[clap(long)]
pub(crate) announce_address: Option<url::Url>,
/// Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
/// default: None - config value will be used instead
#[clap(long)]
pub(crate) monitor_credentials_mode: Option<bool>,
// /// Ephemera configuration arguments.
// #[command(flatten)]
// pub(crate) ephemera_args: ephemera::cli::init::Cmd,
}
pub(crate) async fn execute(args: Args) -> Result<(), Box<dyn Error + Send + Sync>> {
let save_to_file = args.save_config;
let config = build_config(args)?;
pub(crate) async fn execute(args: Args) -> anyhow::Result<()> {
// args take precedence over env
let config = try_load_current_config(&args.id)?
.override_with_env()
.override_with_args(args);
// if we just wanted to write data to the config, exit, don't start any tasks
if save_to_file {
info!("Saving the configuration to a file");
config.save_to_default_location()?;
config
.get_ephemera_args()
.cmd
.clone()
.execute(Some(&config.get_id()));
return Ok(());
}
config.validate()?;
let shutdown_handlers = start_nym_api_tasks(config).await?;
@@ -120,5 +74,10 @@ pub(crate) async fn execute(args: Args) -> Result<(), Box<dyn Error + Send + Syn
log::info!("Stopping nym API");
shutdown_handlers.rocket_handle.notify();
res
if let Err(err) = res {
// that's a nasty workaround, but anyhow errors are generally nicer, especially on exit
bail!("{err}")
}
Ok(())
}
+28 -37
View File
@@ -1,42 +1,26 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::support::config::old_config_v1_1_21::ConfigV1_1_21;
use crate::support::config::old_config_v1_1_27::ConfigV1_1_27;
use crate::support::config::{default_config_directory, default_data_directory, Config};
use anyhow::Result;
use crate::support::config;
use crate::support::config::{
default_config_directory, default_data_directory, upgrade_helpers, Config,
};
use anyhow::{Context, Result};
use nym_crypto::asymmetric::identity;
use rand_07::rngs::OsRng;
use std::{fs, io};
fn try_upgrade_v1_1_21_config(id: &str) -> Result<()> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
fn init_identity_keys(config: &config::NymApiPaths) -> Result<()> {
let keypaths = nym_pemstore::KeyPairPath::new(
&config.private_identity_key_file,
&config.public_identity_key_file,
);
// explicitly load it as v1.1.21 (which is incompatible with the current, i.e. 1.1.22+)
let Ok(old_config) = ConfigV1_1_21::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(());
};
info!("It seems the nym-api is using <= v1.1.21 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
Ok(updated.save_to_default_location()?)
}
fn try_upgrade_v1_1_27_config(id: &str) -> Result<()> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.27 (which is incompatible with the current, i.e. 1.1.28+)
let Ok(old_config) = ConfigV1_1_27::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(());
};
info!("It seems the nym-api is using <= v1.1.27 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
Ok(updated.save_to_default_location()?)
let mut rng = OsRng;
let keypair = identity::KeyPair::new(&mut rng);
nym_pemstore::store_keypair(&keypair, &keypaths)
.context("failed to store identity keys of the nym api")?;
Ok(())
}
fn init_paths(id: &str) -> io::Result<()> {
@@ -46,14 +30,21 @@ fn init_paths(id: &str) -> io::Result<()> {
pub(crate) fn initialise_new(id: &str) -> Result<Config> {
let config = Config::new(id);
// create base storage paths
init_paths(id)?;
// create identity keys
init_identity_keys(&config.base.storage_paths)?;
// create DKG BTE keys
crate::coconut::dkg::controller::init_keypair(&config.coconut_signer)?;
Ok(config)
}
pub(crate) fn try_load_current_config(id: &str) -> Result<Config> {
try_upgrade_v1_1_21_config(id)?;
try_upgrade_v1_1_27_config(id)?;
Ok(Config::read_from_default_path(id)?)
upgrade_helpers::try_upgrade_config(id)?;
Config::read_from_default_path(id).context(
"failed to load config.toml from the default path - are you sure you run `init` before?",
)
}
+95 -140
View File
@@ -4,13 +4,16 @@
use crate::support::config::persistence::{
CoconutSignerPaths, NetworkMonitorPaths, NodeStatusAPIPaths, NymApiPaths,
};
use crate::support::config::r#override::OverrideConfig;
use crate::support::config::template::CONFIG_TEMPLATE;
use nym_config::defaults::{mainnet, NymNetworkDetails};
use anyhow::bail;
use nym_config::defaults::mainnet::read_parsed_var_if_not_default;
use nym_config::defaults::var_names::{CONFIGURED, NYXD};
use nym_config::serde_helpers::de_maybe_stringified;
use nym_config::{
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, DEFAULT_NYM_APIS_DIR, NYM_DIR,
};
use nym_validator_client::nyxd;
use serde::{Deserialize, Serialize};
use std::io;
use std::path::{Path, PathBuf};
@@ -19,14 +22,13 @@ use url::Url;
use zeroize::{Zeroize, ZeroizeOnDrop};
pub(crate) mod helpers;
pub(crate) mod old_config_v1_1_21;
pub(crate) mod old_config_v1_1_27;
mod r#override;
mod persistence;
mod template;
mod upgrade_helpers;
pub const DEFAULT_LOCAL_VALIDATOR: &str = "http://localhost:26657";
pub const DEFAULT_NYM_API_PORT: u16 = 8080;
pub const DEFAULT_DKG_CONTRACT_POLLING_RATE: Duration = Duration::from_secs(10);
@@ -86,6 +88,10 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct Config {
// additional metadata holding on-disk location of this config file
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
pub base: Base,
// TODO: perhaps introduce separate 'path finder' field for all the paths and directories like we have with other configs
@@ -104,16 +110,6 @@ pub struct Config {
pub ephemera: Ephemera,
}
impl<'a> From<&'a Config> for NymNetworkDetails {
fn from(value: &'a Config) -> Self {
// we get the current environmental details and then overwrite whatever is appropriate with
// the values from the config
NymNetworkDetails::new_from_env()
.with_mixnet_contract(Some(value.get_mixnet_contract_address().as_ref()))
.with_vesting_contract(Some(value.get_vesting_contract_address().as_ref()))
}
}
impl NymConfigTemplate for Config {
fn template(&self) -> &'static str {
CONFIG_TEMPLATE
@@ -123,6 +119,7 @@ impl NymConfigTemplate for Config {
impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
save_path: None,
base: Base::new_default(id.as_ref()),
network_monitor: NetworkMonitor::new_default(id.as_ref()),
node_status_api: NodeStatusAPI::new_default(id.as_ref()),
@@ -134,12 +131,78 @@ impl Config {
}
}
pub fn validate(&self) -> anyhow::Result<()> {
let can_sign = self.base.mnemonic.is_some();
if !can_sign && self.rewarding.enabled {
bail!("can't enable rewarding without providing a mnemonic")
}
if !can_sign && self.coconut_signer.enabled {
bail!("can't enable coconut signer without providing a mnemonic")
}
if !can_sign && self.ephemera.enabled {
bail!("can't enable ephemera without providing a mnemonic")
}
Ok(())
}
pub fn override_with_args<O: Into<OverrideConfig>>(mut self, args: O) -> Self {
let args = args.into();
if let Some(enabled_monitor) = args.enable_monitor {
self.network_monitor.enabled = enabled_monitor;
}
if let Some(enable_rewarding) = args.enable_rewarding {
self.rewarding.enabled = enable_rewarding;
}
if let Some(nyxd_upstream) = args.nyxd_validator {
self.base.local_validator = nyxd_upstream;
}
if let Some(mnemonic) = args.mnemonic {
self.base.mnemonic = Some(mnemonic)
}
if let Some(enable_zk_nym) = args.enable_zk_nym {
self.coconut_signer.enabled = enable_zk_nym
}
if let Some(announce_address) = args.announce_address {
self.coconut_signer.announce_address = Some(announce_address)
}
if let Some(monitor_credentials_mode) = args.monitor_credentials_mode {
self.network_monitor.debug.disabled_credentials_mode = !monitor_credentials_mode
}
self
}
pub fn override_with_env(mut self) -> Self {
if std::env::var(CONFIGURED).is_ok() {
// currently the only value that can be overridden is 'nyxd'
if let Some(Ok(custom_nyxd)) = read_parsed_var_if_not_default(NYXD) {
self.base.local_validator = custom_nyxd
}
}
self
}
// simple wrapper that reads config file and assigns path location
fn read_from_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let path = path.as_ref();
let mut loaded: Config = read_config_from_toml_file(path)?;
loaded.save_path = Some(path.to_path_buf());
debug!("loaded config file from {}", path.display());
Ok(loaded)
}
#[allow(dead_code)]
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
Self::read_from_path(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
Self::read_from_path(default_config_filepath(id))
}
pub fn default_location(&self) -> PathBuf {
@@ -151,93 +214,13 @@ impl Config {
save_formatted_config_to_file(self, config_save_location)
}
pub fn get_network_details(&self) -> NymNetworkDetails {
self.into()
}
pub fn with_network_monitor_enabled(mut self, enabled: bool) -> Self {
self.network_monitor.enabled = enabled;
self
}
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
self.network_monitor.debug.disabled_credentials_mode = disabled_credentials_mode;
self
}
pub fn with_rewarding_enabled(mut self, enabled: bool) -> Self {
self.rewarding.enabled = enabled;
self
}
pub fn with_coconut_signer_enabled(mut self, enabled: bool) -> Self {
self.coconut_signer.enabled = enabled;
self
}
pub fn with_ephemera_enabled(mut self, enabled: bool) -> Self {
self.ephemera.enabled = enabled;
self
}
pub fn with_custom_nyxd_validator(mut self, validator: Url) -> Self {
self.base.local_validator = validator;
self
}
pub fn with_announce_address(mut self, announce_address: Url) -> Self {
self.coconut_signer.announce_address = announce_address;
self
}
pub fn with_custom_mixnet_contract(mut self, mixnet_contract: nyxd::AccountId) -> Self {
self.base.mixnet_contract_address = mixnet_contract;
self
}
pub fn with_custom_vesting_contract(mut self, vesting_contract: nyxd::AccountId) -> Self {
self.base.vesting_contract_address = vesting_contract;
self
}
pub fn with_mnemonic(mut self, mnemonic: bip39::Mnemonic) -> Self {
self.base.mnemonic = mnemonic;
self
}
pub fn with_minimum_interval_monitor_threshold(mut self, threshold: u8) -> Self {
self.rewarding.debug.minimum_interval_monitor_threshold = threshold;
self
}
pub fn with_min_mixnode_reliability(mut self, min_mixnode_reliability: u8) -> Self {
self.network_monitor.debug.min_mixnode_reliability = min_mixnode_reliability;
self
}
pub fn with_min_gateway_reliability(mut self, min_gateway_reliability: u8) -> Self {
self.network_monitor.debug.min_gateway_reliability = min_gateway_reliability;
self
}
pub fn with_ephemera_ip(mut self, ip: String) -> Self {
self.ephemera.args.cmd.ephemera_ip = Some(ip);
self
}
pub fn with_ephemera_protocol_port(mut self, port: u16) -> Self {
self.ephemera.args.cmd.ephemera_protocol_port = Some(port);
self
}
pub fn with_ephemera_websocket_port(mut self, port: u16) -> Self {
self.ephemera.args.cmd.ephemera_websocket_port = Some(port);
self
}
pub fn with_ephemera_http_api_port(mut self, port: u16) -> Self {
self.ephemera.args.cmd.ephemera_http_api_port = Some(port);
self
pub fn try_save(&self) -> io::Result<()> {
if let Some(save_location) = &self.save_path {
save_formatted_config_to_file(self, save_location)
} else {
debug!("config file save location is unknown. falling back to the default");
self.save_to_default_location()
}
}
pub fn get_id(&self) -> String {
@@ -248,16 +231,8 @@ impl Config {
self.base.local_validator.clone()
}
pub fn get_mixnet_contract_address(&self) -> nyxd::AccountId {
self.base.mixnet_contract_address.clone()
}
pub fn get_vesting_contract_address(&self) -> nyxd::AccountId {
self.base.vesting_contract_address.clone()
}
pub fn get_mnemonic(&self) -> bip39::Mnemonic {
self.base.mnemonic.clone()
pub fn get_mnemonic(&self) -> Option<&bip39::Mnemonic> {
self.base.mnemonic.as_ref()
}
pub fn get_ephemera_args(&self) -> &crate::ephemera::Args {
@@ -278,23 +253,13 @@ pub struct Base {
#[zeroize(skip)]
pub local_validator: Url,
/// Address of the validator contract managing the network
#[zeroize(skip)]
pub mixnet_contract_address: nyxd::AccountId,
/// Address of the vesting contract holding locked tokens
#[zeroize(skip)]
pub vesting_contract_address: nyxd::AccountId,
/// Mnemonic used for rewarding and/or multisig operations
// TODO: similarly to the note in gateway, this should get moved to a separate file
mnemonic: bip39::Mnemonic,
#[serde(deserialize_with = "de_maybe_stringified")]
mnemonic: Option<bip39::Mnemonic>,
/// Storage paths to the common nym-api files
#[zeroize(skip)]
// ideally we wouldn't be using default here, but I really really don't want to be writing
// another big config migration
#[serde(default)]
pub storage_paths: NymApiPaths,
}
@@ -310,10 +275,7 @@ impl Base {
storage_paths: NymApiPaths::new_default(&id),
id,
local_validator: default_validator,
mixnet_contract_address: mainnet::MIXNET_CONTRACT_ADDRESS.parse().unwrap(),
vesting_contract_address: mainnet::VESTING_CONTRACT_ADDRESS.parse().unwrap(),
// this this doesn't make any sense since you really have a mnemonic beforehand...
mnemonic: bip39::Mnemonic::generate(24).unwrap(),
mnemonic: None,
}
}
}
@@ -561,7 +523,8 @@ pub struct CoconutSigner {
/// Specifies whether rewarding service is enabled in this process.
pub enabled: bool,
pub announce_address: Url,
#[serde(deserialize_with = "de_maybe_stringified")]
pub announce_address: Option<Url>,
pub storage_paths: CoconutSignerPaths,
@@ -571,17 +534,9 @@ pub struct CoconutSigner {
impl CoconutSigner {
pub fn new_default<P: AsRef<Path>>(id: P) -> Self {
let default_validator: Url = DEFAULT_LOCAL_VALIDATOR
.parse()
.expect("default local validator is malformed!");
let mut default_announce_address = default_validator;
default_announce_address
.set_port(Some(DEFAULT_NYM_API_PORT))
.expect("default local validator is malformed!");
CoconutSigner {
enabled: false,
announce_address: default_announce_address,
announce_address: None,
storage_paths: CoconutSignerPaths::new_default(id),
debug: Default::default(),
}
@@ -1,374 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::support::config::persistence::{
CoconutSignerPaths, NetworkMonitorPaths, NodeStatusAPIPaths,
};
use crate::support::config::{
Base, CirculatingSupplyCacher, CirculatingSupplyCacherDebug, CoconutSigner, CoconutSignerDebug,
Config, NetworkMonitor, NetworkMonitorDebug, NodeStatusAPI, NodeStatusAPIDebug, Rewarding,
RewardingDebug, TopologyCacher, TopologyCacherDebug,
};
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_validator_client::nyxd;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;
use url::Url;
const DEFAULT_NYM_API_PORT: u16 = 8080;
const MIXNET_CONTRACT_ADDRESS: &str =
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
const DEFAULT_LOCAL_VALIDATOR: &str = "http://localhost:26657";
const DEFAULT_DKG_CONTRACT_POLLING_RATE: Duration = Duration::from_secs(10);
const DEFAULT_GATEWAY_SENDING_RATE: usize = 200;
const DEFAULT_MAX_CONCURRENT_GATEWAY_CLIENTS: usize = 50;
const DEFAULT_PACKET_DELIVERY_TIMEOUT: Duration = Duration::from_secs(20);
const DEFAULT_MONITOR_RUN_INTERVAL: Duration = Duration::from_secs(15 * 60);
const DEFAULT_GATEWAY_PING_INTERVAL: Duration = Duration::from_secs(60);
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
// This timeout value should be big enough to accommodate an initial bandwidth acquirement
const DEFAULT_GATEWAY_CONNECTION_TIMEOUT: Duration = Duration::from_secs(2 * 60);
const DEFAULT_TEST_ROUTES: usize = 3;
const DEFAULT_MINIMUM_TEST_ROUTES: usize = 1;
const DEFAULT_ROUTE_TEST_PACKETS: usize = 1000;
const DEFAULT_PER_NODE_TEST_PACKETS: usize = 3;
const DEFAULT_TOPOLOGY_CACHE_INTERVAL: Duration = Duration::from_secs(30);
const DEFAULT_NODE_STATUS_CACHE_INTERVAL: Duration = Duration::from_secs(120);
const DEFAULT_CIRCULATING_SUPPLY_CACHE_INTERVAL: Duration = Duration::from_secs(3600);
const DEFAULT_MONITOR_THRESHOLD: u8 = 60;
const DEFAULT_MIN_MIXNODE_RELIABILITY: u8 = 50;
const DEFAULT_MIN_GATEWAY_RELIABILITY: u8 = 20;
#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_21 {
#[serde(default)]
base: BaseV1_1_21,
#[serde(default)]
network_monitor: NetworkMonitorV1_1_21,
#[serde(default)]
node_status_api: NodeStatusAPIV1_1_21,
#[serde(default)]
topology_cacher: TopologyCacherV1_1_21,
#[serde(default)]
circulating_supply_cacher: CirculatingSupplyCacherV1_1_21,
#[serde(default)]
rewarding: RewardingV1_1_21,
#[serde(default)]
coconut_signer: CoconutSignerV1_1_21,
}
impl From<ConfigV1_1_21> for Config {
fn from(value: ConfigV1_1_21) -> Self {
// this value was never properly saved (probably a bug)
// so explicitly set it to the default
let dkg_persistent_state_path =
CoconutSignerV1_1_21::default_dkg_persistent_state_path(&value.base.id);
Config {
base: Base {
id: value.base.id,
local_validator: value.base.local_validator,
mixnet_contract_address: value.base.mixnet_contract_address,
vesting_contract_address: value.base.vesting_contract_address,
mnemonic: value.base.mnemonic,
storage_paths: Default::default(),
},
network_monitor: NetworkMonitor {
enabled: value.network_monitor.enabled,
storage_paths: NetworkMonitorPaths {
credentials_database_path: value.network_monitor.credentials_database_path,
},
debug: NetworkMonitorDebug {
min_mixnode_reliability: value.network_monitor.min_mixnode_reliability,
min_gateway_reliability: value.network_monitor.min_gateway_reliability,
disabled_credentials_mode: value.network_monitor.disabled_credentials_mode,
run_interval: value.network_monitor.run_interval,
gateway_ping_interval: value.network_monitor.gateway_ping_interval,
gateway_sending_rate: value.network_monitor.gateway_sending_rate,
max_concurrent_gateway_clients: value
.network_monitor
.max_concurrent_gateway_clients,
gateway_response_timeout: value.network_monitor.gateway_response_timeout,
gateway_connection_timeout: value.network_monitor.gateway_connection_timeout,
packet_delivery_timeout: value.network_monitor.packet_delivery_timeout,
test_routes: value.network_monitor.test_routes,
minimum_test_routes: value.network_monitor.minimum_test_routes,
route_test_packets: value.network_monitor.route_test_packets,
per_node_test_packets: value.network_monitor.per_node_test_packets,
},
},
node_status_api: NodeStatusAPI {
storage_paths: NodeStatusAPIPaths {
database_path: value.node_status_api.database_path,
},
debug: NodeStatusAPIDebug {
caching_interval: value.node_status_api.caching_interval,
},
},
topology_cacher: TopologyCacher {
debug: TopologyCacherDebug {
caching_interval: value.topology_cacher.caching_interval,
..Default::default()
},
},
circulating_supply_cacher: CirculatingSupplyCacher {
enabled: value.circulating_supply_cacher.enabled,
debug: CirculatingSupplyCacherDebug {
caching_interval: value.circulating_supply_cacher.caching_interval,
},
},
rewarding: Rewarding {
enabled: value.rewarding.enabled,
debug: RewardingDebug {
minimum_interval_monitor_threshold: value
.rewarding
.minimum_interval_monitor_threshold,
},
},
coconut_signer: CoconutSigner {
enabled: value.coconut_signer.enabled,
announce_address: value.base.announce_address,
storage_paths: CoconutSignerPaths {
dkg_persistent_state_path,
verification_key_path: value.coconut_signer.verification_key_path,
secret_key_path: value.coconut_signer.secret_key_path,
decryption_key_path: value.coconut_signer.decryption_key_path,
public_key_with_proof_path: value.coconut_signer.public_key_with_proof_path,
},
debug: CoconutSignerDebug {
dkg_contract_polling_rate: value.coconut_signer.dkg_contract_polling_rate,
},
},
ephemera: Default::default(),
}
}
}
impl MigrationNymConfig for ConfigV1_1_21 {
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("nym-api")
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct BaseV1_1_21 {
/// ID specifies the human readable ID of this particular nym-api.
id: String,
local_validator: Url,
/// Address announced to the directory server for the clients to connect to.
// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
// later on by using name resolvable with a DNS query, such as `nymtech.net`.
announce_address: Url,
/// Address of the validator contract managing the network
mixnet_contract_address: nyxd::AccountId,
/// Address of the vesting contract holding locked tokens
vesting_contract_address: nyxd::AccountId,
/// Mnemonic used for rewarding and/or multisig operations
mnemonic: bip39::Mnemonic,
}
impl Default for BaseV1_1_21 {
fn default() -> Self {
let default_validator: Url = DEFAULT_LOCAL_VALIDATOR
.parse()
.expect("default local validator is malformed!");
let mut default_announce_address = default_validator.clone();
default_announce_address
.set_port(Some(DEFAULT_NYM_API_PORT))
.expect("default local validator is malformed!");
BaseV1_1_21 {
id: String::default(),
local_validator: default_validator,
announce_address: default_announce_address,
mixnet_contract_address: MIXNET_CONTRACT_ADDRESS.parse().unwrap(),
vesting_contract_address: VESTING_CONTRACT_ADDRESS.parse().unwrap(),
mnemonic: bip39::Mnemonic::generate(24).unwrap(),
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct NetworkMonitorV1_1_21 {
min_mixnode_reliability: u8, // defaults to 50
min_gateway_reliability: u8, // defaults to 20
enabled: bool,
#[serde(default)]
disabled_credentials_mode: bool,
#[serde(with = "humantime_serde")]
run_interval: Duration,
#[serde(with = "humantime_serde")]
gateway_ping_interval: Duration,
gateway_sending_rate: usize,
max_concurrent_gateway_clients: usize,
#[serde(with = "humantime_serde")]
gateway_response_timeout: Duration,
#[serde(with = "humantime_serde")]
gateway_connection_timeout: Duration,
#[serde(with = "humantime_serde")]
packet_delivery_timeout: Duration,
credentials_database_path: PathBuf,
test_routes: usize,
minimum_test_routes: usize,
route_test_packets: usize,
per_node_test_packets: usize,
}
impl Default for NetworkMonitorV1_1_21 {
fn default() -> Self {
NetworkMonitorV1_1_21 {
min_mixnode_reliability: DEFAULT_MIN_MIXNODE_RELIABILITY,
min_gateway_reliability: DEFAULT_MIN_GATEWAY_RELIABILITY,
enabled: false,
disabled_credentials_mode: true,
run_interval: DEFAULT_MONITOR_RUN_INTERVAL,
gateway_ping_interval: DEFAULT_GATEWAY_PING_INTERVAL,
gateway_sending_rate: DEFAULT_GATEWAY_SENDING_RATE,
max_concurrent_gateway_clients: DEFAULT_MAX_CONCURRENT_GATEWAY_CLIENTS,
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
gateway_connection_timeout: DEFAULT_GATEWAY_CONNECTION_TIMEOUT,
packet_delivery_timeout: DEFAULT_PACKET_DELIVERY_TIMEOUT,
credentials_database_path: Default::default(),
test_routes: DEFAULT_TEST_ROUTES,
minimum_test_routes: DEFAULT_MINIMUM_TEST_ROUTES,
route_test_packets: DEFAULT_ROUTE_TEST_PACKETS,
per_node_test_packets: DEFAULT_PER_NODE_TEST_PACKETS,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct NodeStatusAPIV1_1_21 {
database_path: PathBuf,
#[serde(with = "humantime_serde")]
caching_interval: Duration,
}
impl Default for NodeStatusAPIV1_1_21 {
fn default() -> Self {
NodeStatusAPIV1_1_21 {
database_path: Default::default(),
caching_interval: DEFAULT_NODE_STATUS_CACHE_INTERVAL,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct TopologyCacherV1_1_21 {
#[serde(with = "humantime_serde")]
caching_interval: Duration,
}
impl Default for TopologyCacherV1_1_21 {
fn default() -> Self {
TopologyCacherV1_1_21 {
caching_interval: DEFAULT_TOPOLOGY_CACHE_INTERVAL,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct CirculatingSupplyCacherV1_1_21 {
enabled: bool,
#[serde(with = "humantime_serde")]
caching_interval: Duration,
}
impl Default for CirculatingSupplyCacherV1_1_21 {
fn default() -> Self {
CirculatingSupplyCacherV1_1_21 {
enabled: true,
caching_interval: DEFAULT_CIRCULATING_SUPPLY_CACHE_INTERVAL,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct RewardingV1_1_21 {
enabled: bool,
minimum_interval_monitor_threshold: u8,
}
impl Default for RewardingV1_1_21 {
fn default() -> Self {
RewardingV1_1_21 {
enabled: false,
minimum_interval_monitor_threshold: DEFAULT_MONITOR_THRESHOLD,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct CoconutSignerV1_1_21 {
enabled: bool,
dkg_persistent_state_path: PathBuf,
verification_key_path: PathBuf,
secret_key_path: PathBuf,
decryption_key_path: PathBuf,
public_key_with_proof_path: PathBuf,
dkg_contract_polling_rate: Duration,
}
impl CoconutSignerV1_1_21 {
pub const DKG_PERSISTENT_STATE_FILE: &'static str = "dkg_persistent_state.json";
fn default_dkg_persistent_state_path(id: &str) -> PathBuf {
ConfigV1_1_21::default_data_directory(id).join(Self::DKG_PERSISTENT_STATE_FILE)
}
}
impl Default for CoconutSignerV1_1_21 {
fn default() -> Self {
Self {
enabled: Default::default(),
dkg_persistent_state_path: Default::default(),
verification_key_path: Default::default(),
secret_key_path: Default::default(),
decryption_key_path: Default::default(),
public_key_with_proof_path: Default::default(),
dkg_contract_polling_rate: DEFAULT_DKG_CONTRACT_POLLING_RATE,
}
}
}
@@ -1,269 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::support::config::persistence::{
CoconutSignerPaths, NetworkMonitorPaths, NodeStatusAPIPaths,
};
use crate::support::config::{
Base, CirculatingSupplyCacher, CoconutSigner, CoconutSignerDebug, Config, Ephemera,
NetworkMonitor, NetworkMonitorDebug, NodeStatusAPI, NodeStatusAPIDebug, Rewarding,
TopologyCacher,
};
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_validator_client::nyxd;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use url::Url;
const DEFAULT_NYM_API_PORT: u16 = 8080;
const MIXNET_CONTRACT_ADDRESS: &str =
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
const DEFAULT_LOCAL_VALIDATOR: &str = "http://localhost:26657";
#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_27 {
#[serde(default)]
base: BaseV1_1_27,
#[serde(default)]
network_monitor: NetworkMonitorV1_1_27,
#[serde(default)]
node_status_api: NodeStatusAPIV1_1_27,
#[serde(default)]
topology_cacher: TopologyCacher,
#[serde(default)]
circulating_supply_cacher: CirculatingSupplyCacher,
#[serde(default)]
rewarding: Rewarding,
#[serde(default)]
coconut_signer: CoconutSignerV1_1_27,
}
impl From<ConfigV1_1_27> for Config {
fn from(value: ConfigV1_1_27) -> Self {
// this value was never properly saved (probably a bug)
// so explicitly set it to the default
Config {
base: Base {
id: value.base.id.clone(),
local_validator: value.base.local_validator,
mixnet_contract_address: value.base.mixnet_contract_address,
vesting_contract_address: value.base.vesting_contract_address,
mnemonic: value.base.mnemonic,
storage_paths: Default::default(),
},
network_monitor: NetworkMonitor {
enabled: value.network_monitor.enabled,
storage_paths: NetworkMonitorPaths {
credentials_database_path: value
.network_monitor
.storage_paths
.credentials_database_path,
},
debug: NetworkMonitorDebug {
min_mixnode_reliability: value.network_monitor.debug.min_mixnode_reliability,
min_gateway_reliability: value.network_monitor.debug.min_gateway_reliability,
disabled_credentials_mode: value
.network_monitor
.debug
.disabled_credentials_mode,
run_interval: value.network_monitor.debug.run_interval,
gateway_ping_interval: value.network_monitor.debug.gateway_ping_interval,
gateway_sending_rate: value.network_monitor.debug.gateway_sending_rate,
max_concurrent_gateway_clients: value
.network_monitor
.debug
.max_concurrent_gateway_clients,
gateway_response_timeout: value.network_monitor.debug.gateway_response_timeout,
gateway_connection_timeout: value
.network_monitor
.debug
.gateway_connection_timeout,
packet_delivery_timeout: value.network_monitor.debug.packet_delivery_timeout,
test_routes: value.network_monitor.debug.test_routes,
minimum_test_routes: value.network_monitor.debug.minimum_test_routes,
route_test_packets: value.network_monitor.debug.route_test_packets,
per_node_test_packets: value.network_monitor.debug.per_node_test_packets,
},
},
node_status_api: NodeStatusAPI {
storage_paths: NodeStatusAPIPaths {
database_path: value.node_status_api.storage_paths.database_path,
},
debug: NodeStatusAPIDebug {
caching_interval: value.node_status_api.debug.caching_interval,
},
},
topology_cacher: value.topology_cacher,
circulating_supply_cacher: value.circulating_supply_cacher,
rewarding: value.rewarding,
coconut_signer: CoconutSigner {
enabled: value.coconut_signer.enabled,
announce_address: value.base.announce_address,
storage_paths: CoconutSignerPaths {
dkg_persistent_state_path: value
.coconut_signer
.storage_paths
.dkg_persistent_state_path,
verification_key_path: value.coconut_signer.storage_paths.verification_key_path,
secret_key_path: value.coconut_signer.storage_paths.secret_key_path,
decryption_key_path: value.coconut_signer.storage_paths.decryption_key_path,
public_key_with_proof_path: value
.coconut_signer
.storage_paths
.public_key_with_proof_path,
},
debug: CoconutSignerDebug {
dkg_contract_polling_rate: value.coconut_signer.debug.dkg_contract_polling_rate,
},
},
ephemera: Ephemera::new_default(&value.base.id),
}
}
}
impl MigrationNymConfig for ConfigV1_1_27 {
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("nym-api")
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct BaseV1_1_27 {
/// ID specifies the human readable ID of this particular nym-api.
id: String,
local_validator: Url,
/// Address announced to the directory server for the clients to connect to.
// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
// later on by using name resolvable with a DNS query, such as `nymtech.net`.
announce_address: Url,
/// Address of the validator contract managing the network
mixnet_contract_address: nyxd::AccountId,
/// Address of the vesting contract holding locked tokens
vesting_contract_address: nyxd::AccountId,
/// Mnemonic used for rewarding and/or multisig operations
mnemonic: bip39::Mnemonic,
}
impl Default for BaseV1_1_27 {
fn default() -> Self {
let default_validator: Url = DEFAULT_LOCAL_VALIDATOR
.parse()
.expect("default local validator is malformed!");
let mut default_announce_address = default_validator.clone();
default_announce_address
.set_port(Some(DEFAULT_NYM_API_PORT))
.expect("default local validator is malformed!");
BaseV1_1_27 {
id: String::default(),
local_validator: default_validator,
announce_address: default_announce_address,
mixnet_contract_address: MIXNET_CONTRACT_ADDRESS.parse().unwrap(),
vesting_contract_address: VESTING_CONTRACT_ADDRESS.parse().unwrap(),
mnemonic: bip39::Mnemonic::generate(24).unwrap(),
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct NetworkMonitorV1_1_27 {
/// Specifies whether network monitoring service is enabled in this process.
pub enabled: bool,
pub storage_paths: NetworkMonitorPaths,
#[serde(default)]
pub debug: NetworkMonitorDebug,
}
impl Default for NetworkMonitorV1_1_27 {
fn default() -> Self {
NetworkMonitorV1_1_27 {
enabled: false,
storage_paths: NetworkMonitorPaths {
credentials_database_path: Default::default(),
},
debug: Default::default(),
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct NodeStatusAPIV1_1_27 {
// pub enabled: bool,
pub storage_paths: NodeStatusAPIPaths,
#[serde(default)]
pub debug: NodeStatusAPIDebug,
}
impl Default for NodeStatusAPIV1_1_27 {
fn default() -> Self {
NodeStatusAPIV1_1_27 {
storage_paths: NodeStatusAPIPaths {
database_path: Default::default(),
},
debug: Default::default(),
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct CoconutSignerV1_1_27 {
/// Specifies whether rewarding service is enabled in this process.
pub enabled: bool,
pub announce_address: Url,
pub storage_paths: CoconutSignerPaths,
#[serde(default)]
pub debug: CoconutSignerDebug,
}
impl Default for CoconutSignerV1_1_27 {
fn default() -> Self {
let announce_address: Url = DEFAULT_LOCAL_VALIDATOR
.parse()
.expect("default local validator is malformed!");
CoconutSignerV1_1_27 {
enabled: Default::default(),
announce_address,
storage_paths: CoconutSignerPaths {
dkg_persistent_state_path: Default::default(),
verification_key_path: Default::default(),
secret_key_path: Default::default(),
decryption_key_path: Default::default(),
public_key_with_proof_path: Default::default(),
},
debug: Default::default(),
}
}
}
+57
View File
@@ -0,0 +1,57 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::support::cli::{init, run};
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
/// Specifies whether network monitoring is enabled on this API
pub(crate) enable_monitor: Option<bool>,
/// Specifies whether network rewarding is enabled on this API
pub(crate) enable_rewarding: Option<bool>,
/// Endpoint to nyxd instance used for contract information.
pub(crate) nyxd_validator: Option<url::Url>,
/// Mnemonic of the network monitor used for sending rewarding and zk-nyms transactions
pub(crate) mnemonic: Option<bip39::Mnemonic>,
/// Flag to indicate whether coconut signer authority is enabled on this API
pub(crate) enable_zk_nym: Option<bool>,
/// Announced address that is going to be put in the DKG contract where zk-nym clients will connect
/// to obtain their credentials
pub(crate) announce_address: Option<url::Url>,
/// Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
pub(crate) monitor_credentials_mode: Option<bool>,
}
impl From<init::Args> for OverrideConfig {
fn from(args: init::Args) -> Self {
OverrideConfig {
enable_monitor: Some(args.enable_monitor),
enable_rewarding: Some(args.enable_rewarding),
nyxd_validator: args.nyxd_validator,
mnemonic: args.mnemonic,
enable_zk_nym: Some(args.enable_zk_nym),
announce_address: args.announce_address,
monitor_credentials_mode: Some(args.monitor_credentials_mode),
}
}
}
impl From<run::Args> for OverrideConfig {
fn from(args: run::Args) -> Self {
OverrideConfig {
enable_monitor: args.enable_monitor,
enable_rewarding: args.enable_rewarding,
nyxd_validator: args.nyxd_validator,
mnemonic: args.mnemonic,
enable_zk_nym: args.enable_zk_nym,
announce_address: args.announce_address,
monitor_credentials_mode: args.monitor_credentials_mode,
}
}
}
+10 -43
View File
@@ -2,10 +2,8 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::support::config::default_data_directory;
use anyhow::{anyhow, Context};
use nym_config::serde_helpers::de_maybe_stringified;
use anyhow::Context;
use nym_crypto::asymmetric::identity;
use rand_07::rngs::OsRng;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
@@ -111,12 +109,10 @@ impl CoconutSignerPaths {
#[serde(default)]
pub struct NymApiPaths {
/// Path to file containing private identity key of the nym-api.
#[serde(deserialize_with = "de_maybe_stringified")]
pub private_identity_key_file: Option<PathBuf>,
pub private_identity_key_file: PathBuf,
/// Path to file containing public identity key of the nym-api.
#[serde(deserialize_with = "de_maybe_stringified")]
pub public_identity_key_file: Option<PathBuf>,
pub public_identity_key_file: PathBuf,
}
impl NymApiPaths {
@@ -124,48 +120,19 @@ impl NymApiPaths {
let data_dir = default_data_directory(id);
NymApiPaths {
private_identity_key_file: Some(data_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME)),
public_identity_key_file: Some(data_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME)),
}
}
pub fn generate_identity_if_missing<P: AsRef<Path>>(&mut self, id: P) -> anyhow::Result<bool> {
if self.private_identity_key_file.is_none() {
log::warn!("identity key paths are not set - going to generate a fresh pair!");
let data_dir = default_data_directory(id);
self.private_identity_key_file =
Some(data_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME));
self.public_identity_key_file =
Some(data_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME));
let keypaths = nym_pemstore::KeyPairPath::new(
self.private_identity_key_file.as_ref().unwrap(),
self.public_identity_key_file.as_ref().unwrap(),
);
let mut rng = OsRng;
let keypair = identity::KeyPair::new(&mut rng);
nym_pemstore::store_keypair(&keypair, &keypaths)
.context("failed to store identity keys of the nym api")?;
Ok(true)
} else {
Ok(false)
private_identity_key_file: data_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME),
public_identity_key_file: data_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
}
}
pub fn load_identity(&self) -> anyhow::Result<identity::KeyPair> {
// if somebody has set their private key but removed public key, the panic is totally on them.
let keypaths = nym_pemstore::KeyPairPath::new(
self.private_identity_key_file
.as_ref()
.ok_or(anyhow!("private key path is not specified"))?,
self.public_identity_key_file
.as_ref()
.ok_or(anyhow!("public key path is not specified"))?,
&self.private_identity_key_file,
&self.public_identity_key_file,
);
nym_pemstore::load_keypair(&keypaths).context("failed to load identity keys of the nym api")
nym_pemstore::load_keypair(&keypaths).context(format!(
"failed to load identity keys of the nym api. paths: {keypaths:?}"
))
}
}
+2 -8
View File
@@ -15,21 +15,15 @@ id = '{{ base.id }}'
# Validator server to which the API will be getting information about the network.
local_validator = '{{ base.local_validator }}'
# Address of the validator contract managing the network.
mixnet_contract_address = '{{ base.mixnet_contract_address }}'
# Address of the vesting contract holding locked tokens
vesting_contract_address = '{{ base.vesting_contract_address }}'
# Mnemonic used for rewarding and validator interaction
mnemonic = '{{ base.mnemonic }}'
[base.storage_paths]
# Path to file containing private identity key of the nym-api.
keys.private_identity_key_file = '{{ base.storage_paths.private_identity_key_file }}'
private_identity_key_file = '{{ base.storage_paths.private_identity_key_file }}'
# Path to file containing public identity key of the nym-api.
keys.public_identity_key_file = '{{ base.storage_paths.public_identity_key_file }}'
public_identity_key_file = '{{ base.storage_paths.public_identity_key_file }}'
##### network monitor config options #####
@@ -0,0 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
pub(crate) fn try_upgrade_config(_id: &str) -> anyhow::Result<()> {
Ok(())
}
+166 -150
View File
@@ -16,7 +16,7 @@ use nym_coconut_dkg_common::{
types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId},
verification_key::{ContractVKShare, VerificationKeyShare},
};
use nym_config::defaults::ChainDetails;
use nym_config::defaults::{ChainDetails, NymNetworkDetails};
use nym_contracts_common::dealings::ContractSafeBytes;
use nym_ephemera_common::msg::QueryMsg as EphemeraQueryMsg;
use nym_ephemera_common::types::JsonPeerInfo;
@@ -46,77 +46,122 @@ use nym_validator_client::nyxd::{
hash::{Hash, SHA256_HASH_SIZE},
AccountId, Coin, TendermintTime,
};
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient, QueryHttpRpcNyxdClient};
use nym_vesting_contract_common::AccountVestingCoins;
use serde::Deserialize;
use std::sync::Arc;
use tokio::sync::{RwLock, RwLockReadGuard};
pub(crate) struct Client(pub(crate) Arc<RwLock<DirectSigningHttpRpcNyxdClient>>);
#[macro_export]
macro_rules! query_guard {
($guard:expr, $($op:tt)*) => {{
match &*$guard {
$crate::support::nyxd::ClientInner::Signing(client) => client.$($op)*,
$crate::support::nyxd::ClientInner::Query(client) => client.$($op)*,
}
}};
}
impl Clone for Client {
fn clone(&self) -> Self {
Client(Arc::clone(&self.0))
}
macro_rules! nyxd_query {
($self:expr, $($op:tt)*) => {{
let guard = $self.inner.read().await;
match &*guard {
$crate::support::nyxd::ClientInner::Signing(client) => client.$($op)*,
$crate::support::nyxd::ClientInner::Query(client) => client.$($op)*,
}
}};
}
macro_rules! nyxd_signing_shared {
($self:expr, $($op:tt)*) => {{
let guard = $self.inner.read().await;
match &*guard {
$crate::support::nyxd::ClientInner::Signing(client) => client.$($op)*,
$crate::support::nyxd::ClientInner::Query(_) => panic!("attempted to use a signing method on a query client"),
}
}};
}
macro_rules! nyxd_signing {
($self:expr, $($op:tt)*) => {{
let guard = $self.inner.write().await;
match &*guard {
$crate::support::nyxd::ClientInner::Signing(client) => client.$($op)*,
$crate::support::nyxd::ClientInner::Query(_) => panic!("attempted to use a signing method on a query client"),
}
}};
}
#[derive(Clone)]
pub(crate) struct Client {
inner: Arc<RwLock<ClientInner>>,
}
pub enum ClientInner {
Signing(DirectSigningHttpRpcNyxdClient),
Query(QueryHttpRpcNyxdClient),
}
impl Client {
pub(crate) fn new(config: &Config) -> Self {
let details = config.get_network_details();
let details = NymNetworkDetails::new_from_env();
let nyxd_url = config.get_nyxd_url();
let client_config = nyxd::Config::try_from_nym_network_details(&details)
.expect("failed to construct valid validator client config with the provided network");
let mnemonic = config.get_mnemonic();
let inner = if let Some(mnemonic) = config.get_mnemonic() {
ClientInner::Signing(
DirectSigningHttpRpcNyxdClient::connect_with_mnemonic(
client_config,
nyxd_url.as_str(),
mnemonic.clone(),
)
.expect("Failed to connect to nyxd!"),
)
} else {
ClientInner::Query(
QueryHttpRpcNyxdClient::connect(client_config, nyxd_url.as_str())
.expect("Failed to connect to nyxd!"),
)
};
let inner = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic(
client_config,
nyxd_url.as_str(),
mnemonic,
)
.expect("Failed to connect to nyxd!");
Client(Arc::new(RwLock::new(inner)))
Client {
inner: Arc::new(RwLock::new(inner)),
}
}
pub(crate) async fn read(&self) -> RwLockReadGuard<'_, DirectSigningHttpRpcNyxdClient> {
self.0.read().await
pub(crate) async fn read(&self) -> RwLockReadGuard<'_, ClientInner> {
self.inner.read().await
}
pub(crate) async fn client_address(&self) -> AccountId {
self.0.read().await.address()
nyxd_signing_shared!(self, address())
}
pub(crate) async fn balance<S: Into<String>>(&self, denom: S) -> Result<Coin, NyxdError> {
let guard = self.0.read().await;
let address = self.client_address().await;
let denom = denom.into();
let address = guard.address();
match self
.0
.read()
.await
.get_balance(&address, denom.clone())
.await?
{
let balance = nyxd_query!(self, get_balance(&address, denom.clone()).await?);
match balance {
None => Ok(Coin::new(0, denom)),
Some(coin) => Ok(coin),
}
}
pub(crate) async fn chain_details(&self) -> ChainDetails {
self.0.read().await.current_chain_details().clone()
nyxd_query!(self, current_chain_details().clone())
}
pub(crate) async fn get_rewarding_validator_address(&self) -> Result<AccountId, NyxdError> {
let cosmwasm_addr = self
.0
.read()
.await
.get_mixnet_contract_state()
.await?
.rewarding_validator_address
.into_string();
let cosmwasm_addr = nyxd_query!(
self,
get_mixnet_contract_state()
.await?
.rewarding_validator_address
.into_string()
);
// this should never fail otherwise it implies either
// 1) our mixnet contract state is invalid
@@ -132,7 +177,7 @@ impl Client {
// a helper function for the future to obtain the current block timestamp
#[allow(dead_code)]
pub(crate) async fn current_block_timestamp(&self) -> Result<TendermintTime, NyxdError> {
let time = self.0.read().await.get_current_block_timestamp().await?;
let time = nyxd_query!(self, get_current_block_timestamp().await?);
Ok(time)
}
@@ -148,7 +193,7 @@ impl Client {
&self,
height: u32,
) -> Result<Option<[u8; SHA256_HASH_SIZE]>, NyxdError> {
let hash = match self.0.read().await.get_block_hash(height).await? {
let hash = match nyxd_query!(self, get_block_hash(height).await?) {
Hash::Sha256(hash) => Some(hash),
Hash::None => None,
};
@@ -157,44 +202,46 @@ impl Client {
}
pub(crate) async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NyxdError> {
self.0.read().await.get_all_mixnodes_detailed().await
nyxd_query!(self, get_all_mixnodes_detailed().await)
}
pub(crate) async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NyxdError> {
self.0.read().await.get_all_gateways().await
nyxd_query!(self, get_all_gateways().await)
}
pub(crate) async fn get_current_interval(&self) -> Result<CurrentIntervalResponse, NyxdError> {
self.0.read().await.get_current_interval_details().await
nyxd_query!(self, get_current_interval_details().await)
}
pub(crate) async fn get_current_epoch_status(&self) -> Result<EpochStatus, NyxdError> {
self.0.read().await.get_current_epoch_status().await
nyxd_query!(self, get_current_epoch_status().await)
}
pub(crate) async fn get_current_rewarding_parameters(
&self,
) -> Result<RewardingParams, NyxdError> {
self.0.read().await.get_rewarding_parameters().await
nyxd_query!(self, get_rewarding_parameters().await)
}
pub(crate) async fn get_rewarded_set_mixnodes(
&self,
) -> Result<Vec<(MixId, RewardedSetNodeStatus)>, NyxdError> {
self.0.read().await.get_all_rewarded_set_mixnodes().await
nyxd_query!(self, get_all_rewarded_set_mixnodes().await)
}
pub(crate) async fn get_current_vesting_account_storage_key(&self) -> Result<u32, NyxdError> {
let guard = self.0.read().await;
let guard = self.inner.read().await;
// the expect is fine as we always construct the client with the vesting contract explicitly set
let vesting_contract = guard
.vesting_contract_address()
.expect("vesting contract address is not available");
let vesting_contract = query_guard!(
guard,
vesting_contract_address().expect("vesting contract address is not available")
);
// TODO: I don't like the usage of the hardcoded value here
let res = guard
.query_contract_raw(vesting_contract, b"key".to_vec())
.await?;
let res = query_guard!(
guard,
query_contract_raw(vesting_contract, b"key".to_vec()).await?
);
if res.is_empty() {
return Ok(0);
}
@@ -205,22 +252,22 @@ impl Client {
pub(crate) async fn get_all_vesting_coins(
&self,
) -> Result<Vec<AccountVestingCoins>, NyxdError> {
self.0.read().await.get_all_accounts_vesting_coins().await
nyxd_query!(self, get_all_accounts_vesting_coins().await)
}
pub(crate) async fn get_all_family_members(
&self,
) -> Result<Vec<(IdentityKey, FamilyHead)>, NyxdError> {
self.0.read().await.get_all_family_members().await
nyxd_query!(self, get_all_family_members().await)
}
pub(crate) async fn get_pending_events_count(&self) -> Result<u32, NyxdError> {
let pending = self.0.read().await.get_number_of_pending_events().await?;
let pending = nyxd_query!(self, get_number_of_pending_events().await?);
Ok(pending.epoch_events + pending.interval_events)
}
pub(crate) async fn begin_epoch_transition(&self) -> Result<(), NyxdError> {
self.0.write().await.begin_epoch_transition(None).await?;
nyxd_signing!(self, begin_epoch_transition(None).await?);
Ok(())
}
@@ -247,26 +294,29 @@ impl Client {
}
}
// "technically" we don't need a write access to the client,
// but we REALLY don't want to accidentally send any transactions while we're sending rewarding messages
// as that would have messed up sequence numbers
let guard = self.0.write().await;
// the expect is fine as we always construct the client with the mixnet contract explicitly set
let mixnet_contract = guard
.mixnet_contract_address()
.expect("mixnet contract address is not available");
let mixnet_contract = nyxd_query!(
self,
mixnet_contract_address()
.expect("mixnet contract address is not available")
.clone()
);
let msgs = generate_reward_messages(nodes);
guard
.execute_multiple(
mixnet_contract,
// "technically" we don't need a write access to the client,
// but we REALLY don't want to accidentally send any transactions while we're sending rewarding messages
// as that would have messed up sequence numbers
nyxd_signing!(
self,
execute_multiple(
&mixnet_contract,
msgs,
Default::default(),
format!("rewarding {} mixnodes", nodes.len()),
)
.await?;
.await?
);
Ok(())
}
@@ -275,20 +325,15 @@ impl Client {
new_rewarded_set: Vec<LayerAssignment>,
expected_active_set_size: u32,
) -> Result<(), NyxdError> {
self.0
.write()
.await
.advance_current_epoch(new_rewarded_set, expected_active_set_size, None)
.await?;
nyxd_signing!(
self,
advance_current_epoch(new_rewarded_set, expected_active_set_size, None).await?
);
Ok(())
}
pub(crate) async fn reconcile_epoch_events(&self, limit: Option<u32>) -> Result<(), NyxdError> {
self.0
.write()
.await
.reconcile_epoch_events(limit, None)
.await?;
nyxd_signing!(self, reconcile_epoch_events(limit, None).await?);
Ok(())
}
}
@@ -300,7 +345,7 @@ impl crate::coconut::client::Client for Client {
}
async fn get_tx(&self, tx_hash: Hash) -> crate::coconut::error::Result<nyxd::TxResponse> {
self.0.read().await.get_tx(tx_hash).await.map_err(|source| {
nyxd_query!(self, get_tx(tx_hash).await).map_err(|source| {
CoconutError::TxRetrievalFailure {
tx_hash: tx_hash.to_string(),
source,
@@ -312,78 +357,69 @@ impl crate::coconut::client::Client for Client {
&self,
proposal_id: u64,
) -> crate::coconut::error::Result<ProposalResponse> {
Ok(self.0.read().await.query_proposal(proposal_id).await?)
Ok(nyxd_query!(self, query_proposal(proposal_id).await?))
}
async fn list_proposals(&self) -> crate::coconut::error::Result<Vec<ProposalResponse>> {
Ok(self.0.read().await.get_all_proposals().await?)
Ok(nyxd_query!(self, get_all_proposals().await?))
}
async fn get_spent_credential(
&self,
blinded_serial_number: String,
) -> crate::coconut::error::Result<SpendCredentialResponse> {
Ok(self
.0
.read()
.await
.get_spent_credential(blinded_serial_number)
.await?)
Ok(nyxd_query!(
self,
get_spent_credential(blinded_serial_number).await?
))
}
async fn get_current_epoch(&self) -> crate::coconut::error::Result<Epoch> {
Ok(self.0.read().await.get_current_epoch().await?)
Ok(nyxd_query!(self, get_current_epoch().await?))
}
async fn group_member(&self, addr: String) -> crate::coconut::error::Result<MemberResponse> {
Ok(self.0.read().await.member(addr, None).await?)
Ok(nyxd_query!(self, member(addr, None).await?))
}
async fn get_current_epoch_threshold(
&self,
) -> crate::coconut::error::Result<Option<nym_dkg::Threshold>> {
Ok(self.0.read().await.get_current_epoch_threshold().await?)
Ok(nyxd_query!(self, get_current_epoch_threshold().await?))
}
async fn get_initial_dealers(
&self,
) -> crate::coconut::error::Result<Option<InitialReplacementData>> {
Ok(self.0.read().await.get_initial_dealers().await?)
Ok(nyxd_query!(self, get_initial_dealers().await?))
}
async fn get_self_registered_dealer_details(
&self,
) -> crate::coconut::error::Result<DealerDetailsResponse> {
let self_address = &self.address().await;
Ok(self.0.read().await.get_dealer_details(self_address).await?)
Ok(nyxd_query!(self, get_dealer_details(self_address).await?))
}
async fn get_current_dealers(&self) -> crate::coconut::error::Result<Vec<DealerDetails>> {
Ok(self.0.read().await.get_all_current_dealers().await?)
Ok(nyxd_query!(self, get_all_current_dealers().await?))
}
async fn get_dealings(
&self,
idx: usize,
) -> crate::coconut::error::Result<Vec<ContractDealing>> {
Ok(self
.0
.read()
.await
.get_all_epoch_dealings(idx as u64)
.await?)
Ok(nyxd_query!(self, get_all_epoch_dealings(idx as u64).await?))
}
async fn get_verification_key_shares(
&self,
epoch_id: EpochId,
) -> crate::coconut::error::Result<Vec<ContractVKShare>> {
Ok(self
.0
.read()
.await
.get_all_verification_key_shares(epoch_id)
.await?)
Ok(nyxd_query!(
self,
get_all_verification_key_shares(epoch_id).await?
))
}
async fn vote_proposal(
@@ -392,25 +428,17 @@ impl crate::coconut::client::Client for Client {
vote_yes: bool,
fee: Option<Fee>,
) -> Result<(), CoconutError> {
self.0
.write()
.await
.vote_proposal(proposal_id, vote_yes, fee)
.await?;
nyxd_signing!(self, vote_proposal(proposal_id, vote_yes, fee).await?);
Ok(())
}
async fn execute_proposal(&self, proposal_id: u64) -> crate::coconut::error::Result<()> {
self.0
.write()
.await
.execute_proposal(proposal_id, None)
.await?;
nyxd_signing!(self, execute_proposal(proposal_id, None).await?);
Ok(())
}
async fn advance_epoch_state(&self) -> crate::coconut::error::Result<()> {
self.0.write().await.advance_dkg_epoch_state(None).await?;
nyxd_signing!(self, advance_dkg_epoch_state(None).await?);
Ok(())
}
@@ -420,12 +448,10 @@ impl crate::coconut::client::Client for Client {
announce_address: String,
resharing: bool,
) -> Result<ExecuteResult, CoconutError> {
Ok(self
.0
.write()
.await
.register_dealer(bte_key, announce_address, resharing, None)
.await?)
Ok(nyxd_signing!(
self,
register_dealer(bte_key, announce_address, resharing, None).await?
))
}
async fn submit_dealing(
@@ -433,12 +459,10 @@ impl crate::coconut::client::Client for Client {
dealing_bytes: ContractSafeBytes,
resharing: bool,
) -> Result<ExecuteResult, CoconutError> {
Ok(self
.0
.write()
.await
.submit_dealing_bytes(dealing_bytes, resharing, None)
.await?)
Ok(nyxd_signing!(
self,
submit_dealing_bytes(dealing_bytes, resharing, None).await?
))
}
async fn submit_verification_key_share(
@@ -446,31 +470,27 @@ impl crate::coconut::client::Client for Client {
share: VerificationKeyShare,
resharing: bool,
) -> crate::coconut::error::Result<ExecuteResult> {
Ok(self
.0
.write()
.await
.submit_verification_key_share(share, resharing, None)
.await?)
Ok(nyxd_signing!(
self,
submit_verification_key_share(share, resharing, None).await?
))
}
}
#[async_trait]
impl crate::ephemera::client::Client for Client {
async fn get_ephemera_peers(&self) -> crate::ephemera::error::Result<Vec<JsonPeerInfo>> {
Ok(self.0.read().await.get_all_ephemera_peers().await?)
Ok(nyxd_query!(self, get_all_ephemera_peers().await?))
}
async fn register_ephemera_peer(
&self,
peer_info: JsonPeerInfo,
) -> crate::ephemera::error::Result<ExecuteResult> {
Ok(self
.0
.write()
.await
.register_as_peer(peer_info, None)
.await?)
Ok(nyxd_signing!(
self,
register_as_peer(peer_info, None).await?
))
}
}
@@ -480,7 +500,7 @@ impl DkgQueryClient for Client {
where
for<'a> T: Deserialize<'a>,
{
self.0.read().await.query_dkg_contract(query).await
nyxd_query!(self, query_dkg_contract(query).await)
}
}
@@ -493,7 +513,7 @@ impl EphemeraQueryClient for Client {
where
for<'a> T: Deserialize<'a>,
{
self.0.read().await.query_ephemera_contract(query).await
nyxd_query!(self, query_ephemera_contract(query).await)
}
}
@@ -506,11 +526,7 @@ impl SpDirectoryQueryClient for Client {
where
for<'a> T: Deserialize<'a>,
{
self.0
.read()
.await
.query_service_provider_contract(query)
.await
nyxd_query!(self, query_service_provider_contract(query).await)
}
}
@@ -523,6 +539,6 @@ impl NameServiceQueryClient for Client {
where
for<'a> T: Deserialize<'a>,
{
self.0.read().await.query_name_service_contract(query).await
nyxd_query!(self, query_name_service_contract(query).await)
}
}