NR config migration

This commit is contained in:
Jędrzej Stuczyński
2023-06-02 15:00:17 +01:00
parent f8ce87a205
commit 3916bbf632
9 changed files with 248 additions and 166 deletions
@@ -12,8 +12,6 @@ use std::str::FromStr;
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion;
// pub mod old_config_v1_1_13;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
@@ -140,26 +138,6 @@ impl Socks5 {
}
}
// pub fn with_port(&mut self, port: u16) {
// self.listening_port = port;
// }
//
// pub fn with_provider_mix_address(&mut self, address: String) {
// self.provider_mix_address = address;
// }
//
// pub fn with_provider_interface_version(&mut self, version: ProviderInterfaceVersion) {
// self.provider_interface_version = version;
// }
//
// pub fn with_socks5_protocol_version(&mut self, version: Socks5ProtocolVersion) {
// self.socks5_protocol_version = version;
// }
//
// pub fn with_anonymous_replies(&mut self, anonymous_replies: bool) {
// self.send_anonymously = anonymous_replies;
// }
pub fn get_provider_mix_address(&self) -> Recipient {
Recipient::try_from_base58_string(&self.provider_mix_address)
.expect("malformed provider address")
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli::try_upgrade_v1_1_13_config;
use crate::cli::try_upgrade_config;
use crate::config::{default_config_directory, default_config_filepath, default_data_directory};
use crate::{
cli::{override_config, OverrideConfig},
@@ -109,7 +109,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> {
let already_init = if default_config_filepath(id).exists() {
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
try_upgrade_config(id)?;
eprintln!("Client \"{id}\" was already initialised before");
true
} else {
@@ -2,12 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_19::ConfigV1_1_19;
use crate::{
config::{BaseClientConfig, Config},
error::NetworkRequesterError,
};
use clap::{CommandFactory, Parser, Subcommand};
use log::info;
use log::{error, info};
use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell};
@@ -98,19 +99,68 @@ pub(crate) async fn execute(args: Cli) -> Result<(), NetworkRequesterError> {
Ok(())
}
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
todo!()
// // explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
// let Ok(old_config) = OldConfigV1_1_13::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 client is using <= v1.1.13 config template.");
// info!("It is going to get updated to the current specification.");
//
// let updated: Config = old_config.into();
// updated.save_to_file(None)
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, NetworkRequesterError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.13 (which is incompatible with the next step, i.e. 1.1.19)
let Ok(old_config) = OldConfigV1_1_13::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(false);
};
info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_19 = old_config.into();
let updated: Config = updated_step1.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_19_config(id: &str) -> Result<bool, NetworkRequesterError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.19 (which is incompatible with the current one, i.e. +1.1.20)
let Ok(old_config) = ConfigV1_1_19::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(false);
};
info!("It seems the client is using <= v1.1.19 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), NetworkRequesterError> {
let upgraded = try_upgrade_v1_1_13_config(id)?;
if !upgraded {
try_upgrade_v1_1_19_config(id)?;
}
Ok(())
}
fn try_load_current_config(id: &str) -> Result<Config, NetworkRequesterError> {
try_upgrade_config(id)?;
let config = match Config::read_from_default_path(id) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})");
return Err(NetworkRequesterError::FailedToLoadConfig(id.to_string()));
}
};
if !config.validate() {
return Err(NetworkRequesterError::ConfigValidationFailure);
}
Ok(config)
}
#[cfg(test)]
@@ -1,13 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::cli::try_upgrade_v1_1_13_config;
use crate::cli::try_load_current_config;
use crate::{
cli::{override_config, OverrideConfig},
config::Config,
error::NetworkRequesterError,
};
use clap::Args;
use log::error;
use nym_bin_common::version_checker;
use nym_sphinx::addressing::clients::Recipient;
@@ -107,33 +108,11 @@ pub(crate) async fn execute(args: &Run) -> Result<(), NetworkRequesterError> {
);
}
let id = &args.id;
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
let mut config = match Config::read_from_default_path(id) {
Ok(cfg) => cfg,
Err(err) => {
log::error!(
"Failed to load config for {}. \
Are you sure you have run `init` before? (Error was: {err})",
id
);
return Err(NetworkRequesterError::FailedToLoadConfig(id.to_string()));
}
};
if !config.validate() {
return Err(NetworkRequesterError::ConfigValidationFailure);
}
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
let mut config = try_load_current_config(&args.id)?;
config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
log::error!("Failed the local version check");
error!("failed the local version check");
return Err(NetworkRequesterError::FailedLocalVersionCheck);
}
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
use nym_config::{
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
OptionalSet, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
@@ -18,6 +19,7 @@ pub use nym_client_core::config::Config as BaseClientConfig;
pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig};
pub mod old_config_v1_1_13;
pub mod old_config_v1_1_19;
mod persistence;
mod template;
@@ -60,14 +62,14 @@ pub struct Config {
pub base: BaseClientConfig,
#[serde(default)]
pub network_requester_config: NetworkRequester,
pub network_requester: NetworkRequester,
// alias due to backwards compatibility
#[serde(alias = "network_requester")]
pub storage_paths: NetworkRequesterPaths,
#[serde(default)]
pub network_requester_debug: Debug,
pub logging: LoggingSettings,
}
impl NymConfigTemplate for Config {
@@ -80,9 +82,10 @@ impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
base: BaseClientConfig::new(id.as_ref()),
network_requester_config: Default::default(),
network_requester: Default::default(),
storage_paths: NetworkRequesterPaths::new_default(default_data_directory(id.as_ref())),
network_requester_debug: Default::default(),
logging: Default::default(),
}
}
@@ -127,6 +130,7 @@ impl Config {
self
}
#[allow(unused)]
pub fn with_optional_base_env<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
where
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
@@ -137,6 +141,7 @@ impl Config {
self
}
#[allow(unused)]
pub fn with_optional_base_custom_env<F, T, G>(
mut self,
f: F,
@@ -1,61 +1,35 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::config::old_config_v1_1_19::ConfigV1_1_19;
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 {
#[serde(flatten)]
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
}
// impl NymConfig for OldConfigV1_1_13 {
// fn template() -> &'static str {
// // not intended to be used
// unimplemented!()
// }
//
// // TODO: merge base dir with `HostStore`.
// fn default_root_directory() -> PathBuf {
// dirs::home_dir()
// .expect("Failed to evaluate $HOME value")
// .join(".nym")
// .join("service-providers")
// .join("network-requester")
// }
//
// fn try_default_root_directory() -> Option<PathBuf> {
// dirs::home_dir().map(|path| path.join(".nym").join("clients"))
// }
//
// fn root_directory(&self) -> PathBuf {
// self.base.client.nym_root_directory.clone()
// }
//
// fn config_directory(&self) -> PathBuf {
// self.root_directory()
// .join(&self.base.client.id)
// .join("config")
// }
//
// fn data_directory(&self) -> PathBuf {
// self.root_directory()
// .join(&self.base.client.id)
// .join("data")
// }
// }
impl From<OldConfigV1_1_13> for Config {
fn from(value: OldConfigV1_1_13) -> Self {
todo!()
// Config {
// base: value.base.into(),
// network_requester: Default::default(),
// network_requester_debug: Default::default(),
// }
impl MigrationNymConfig for OldConfigV1_1_13 {
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("service-providers")
.join("network-requester")
}
}
impl From<OldConfigV1_1_13> for ConfigV1_1_19 {
fn from(value: OldConfigV1_1_13) -> Self {
ConfigV1_1_19 {
base: value.base.into(),
network_requester: Default::default(),
network_requester_debug: Default::default(),
}
}
}
@@ -0,0 +1,117 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::NetworkRequesterPaths;
use crate::config::{BaseClientConfig, Config, Debug};
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_client_core::config::old_config_v1_1_19::ConfigV1_1_19 as BaseConfigV1_1_19;
use nym_client_core::config::Client;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;
const DEFAULT_STANDARD_LIST_UPDATE_INTERVAL: Duration = Duration::from_secs(30 * 60);
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_19 {
#[serde(flatten)]
pub base: BaseConfigV1_1_19<ConfigV1_1_19>,
#[serde(default)]
pub network_requester: NetworkRequster,
#[serde(default)]
pub network_requester_debug: DebugV1_1_19,
}
impl From<ConfigV1_1_19> for Config {
fn from(value: ConfigV1_1_19) -> Self {
Config {
base: BaseClientConfig {
client: Client {
version: value.base.client.version,
id: value.base.client.id,
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
nyxd_urls: value.base.client.nyxd_urls,
nym_api_urls: value.base.client.nym_api_urls,
gateway_endpoint: value.base.client.gateway_endpoint.into(),
},
debug: Default::default(),
},
network_requester: Default::default(),
storage_paths: NetworkRequesterPaths {
common_paths: CommonClientPaths {
keys: ClientKeysPaths {
private_identity_key_file: value.base.client.private_identity_key_file,
public_identity_key_file: value.base.client.public_identity_key_file,
private_encryption_key_file: value.base.client.private_encryption_key_file,
public_encryption_key_file: value.base.client.public_encryption_key_file,
gateway_shared_key_file: value.base.client.gateway_shared_key_file,
ack_key_file: value.base.client.ack_key_file,
},
credentials_database: value.base.client.database_path,
reply_surb_database: value.base.client.reply_surb_database_path,
},
allowed_list_location: value.network_requester.allowed_list_location,
unknown_list_location: value.network_requester.unknown_list_location,
},
network_requester_debug: value.network_requester_debug.into(),
logging: Default::default(),
}
}
}
impl MigrationNymConfig for ConfigV1_1_19 {
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("service-providers")
.join("network-requester")
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct NetworkRequster {
pub allowed_list_location: PathBuf,
pub unknown_list_location: PathBuf,
}
impl Default for NetworkRequster {
fn default() -> Self {
// same defaults as we had in <= v1.1.13
NetworkRequster {
allowed_list_location: <ConfigV1_1_19 as MigrationNymConfig>::default_root_directory()
.join("allowed.list"),
unknown_list_location: <ConfigV1_1_19 as MigrationNymConfig>::default_root_directory()
.join("unknown.list"),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugV1_1_19 {
#[serde(with = "humantime_serde")]
pub standard_list_update_interval: Duration,
}
impl From<DebugV1_1_19> for Debug {
fn from(value: DebugV1_1_19) -> Self {
Debug {
standard_list_update_interval: value.standard_list_update_interval,
}
}
}
impl Default for DebugV1_1_19 {
fn default() -> Self {
DebugV1_1_19 {
standard_list_update_interval: DEFAULT_STANDARD_LIST_UPDATE_INTERVAL,
}
}
}
@@ -30,23 +30,4 @@ impl NetworkRequesterPaths {
unknown_list_location: base_dir.join(DEFAULT_UNKNOWN_LIST_FILENAME),
}
}
pub fn new_legacy<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
// that's a bit disgusting...
// go from `$HOME/.nym/service-providers/network-requester/<id>/data`
// to `$HOME/.nym/service-providers/network-requester`
let root_dir = base_dir
.parent()
.expect("invalid legacy path configuration")
.parent()
.expect("invalid legacy path configuration");
NetworkRequesterPaths {
common_paths: CommonClientPaths::new_default(base_dir),
allowed_list_location: root_dir.join(DEFAULT_ALLOWED_LIST_FILENAME),
unknown_list_location: root_dir.join(DEFAULT_UNKNOWN_LIST_FILENAME),
}
}
}
@@ -24,48 +24,55 @@ id = '{{ client.id }}'
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
# Addresses to nyxd validators via which the client can communicate with the chain.
nyxd_urls = [{{#each client.nyxd_urls }}
'{{this}}',
{{/each}}]
nyxd_urls = [
{{#each client.nyxd_urls }}
'{{this}}',
{{/each}}
]
# Addresses to APIs running on validator from which the client gets the view of the network.
nym_api_urls = [{{#each client.nym_api_urls }}
'{{this}}',
{{/each}}]
nym_api_urls = [
{{#each client.nym_api_urls }}
'{{this}}',
{{/each}}
]
[storage_paths]
# Path to file containing private identity key.
private_identity_key_file = '{{ client.private_identity_key_file }}'
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
# Path to file containing public identity key.
public_identity_key_file = '{{ client.public_identity_key_file }}'
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
# Path to file containing private encryption key.
private_encryption_key_file = '{{ client.private_encryption_key_file }}'
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}'
# Path to file containing public encryption key.
public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# Path to the database containing bandwidth credentials
database_path = '{{ client.database_path }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database_path = '{{ client.reply_surb_database_path }}'
##### additional client config options #####
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to.
ack_key_file = '{{ client.ack_key_file }}'
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
##### advanced configuration options #####
# Path to the database containing bandwidth credentials
credentials_database = '{{ storage_paths.credentials_database }}'
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ client.nym_root_directory }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# Location of the file containing our allow.list
allowed_list_location = '{{ storage_paths.allowed_list_location }}'
# Location of the file containing our unknown.list
unknown_list_location = '{{ storage_paths.unknown_list_location }}'
# DEPRECATED
[client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
@@ -76,15 +83,6 @@ gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
##### network requester specific config options #####
[network_requester]
# Location of the file containing our allow.list
allowed_list_location = '{{ network_requester.allowed_list_location }}'
# Location of the file containing our unknown.list
unknown_list_location = '{{ network_requester.unknown_list_location }}'
##### logging configuration options #####
[logging]