Skip to content

Commit

Permalink
config migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
jstuczyn committed Oct 30, 2023
1 parent c089067 commit ccd8b3f
Show file tree
Hide file tree
Showing 10 changed files with 450 additions and 61 deletions.
38 changes: 13 additions & 25 deletions mixnode/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright 2020 - Nym Technologies SA <[email protected]>
// SPDX-License-Identifier: Apache-2.0

use crate::config::old_config_v1_1_21::ConfigV1_1_21;
use crate::config::default_config_filepath;
use crate::error::MixnodeError;
use crate::{config::Config, Cli};
use anyhow::anyhow;
use clap::CommandFactory;
use clap::Subcommand;
use colored::Colorize;
Expand All @@ -22,6 +22,7 @@ mod init;
mod node_details;
mod run;
mod sign;
mod upgrade_helpers;

#[derive(Subcommand)]
pub(crate) enum Commands {
Expand Down Expand Up @@ -131,32 +132,19 @@ pub(crate) fn version_check(cfg: &Config) -> bool {
}
}

fn try_upgrade_v1_1_21_config(id: &str) -> std::io::Result<()> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;

// 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 mixnode is using <= v1.1.21 config template.");
info!("It is going to get updated to the current specification.");

let updated: Config = old_config.into();
updated.save_to_default_location()
}

fn try_load_current_config(id: &str) -> anyhow::Result<Config> {
try_upgrade_v1_1_21_config(id)?;
fn try_load_current_config(id: &str) -> Result<Config, MixnodeError> {
upgrade_helpers::try_upgrade_config(id)?;

Config::read_from_default_path(id).map_err(|err| {
let error_msg =
format!(
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
);
error!("{error_msg}");
anyhow!(error_msg)
error!(
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
);
MixnodeError::ConfigLoadFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
}
})
}

Expand Down
66 changes: 66 additions & 0 deletions mixnode/src/commands/upgrade_helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2023 - Nym Technologies SA <[email protected]>
// SPDX-License-Identifier: Apache-2.0

use crate::config::old_config_v1_1_21::ConfigV1_1_21;
use crate::config::old_config_v1_1_32::ConfigV1_1_32;
use crate::config::{default_config_filepath, Config};
use crate::error::MixnodeError;
use log::info;

fn try_upgrade_v1_1_21_config(id: &str) -> Result<bool, MixnodeError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;

// 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(false);
};
info!("It seems the mixnode is using <= v1.1.21 config template.");
info!("It is going to get updated to the current specification.");

let updated_step1: ConfigV1_1_32 = old_config.into();

let updated: Config = updated_step1.into();
updated
.save_to_default_location()
.map_err(|err| MixnodeError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;

Ok(true)
}

fn try_upgrade_v1_1_32_config(id: &str) -> Result<bool, MixnodeError> {
// explicitly load it as v1.1.32 (which is incompatible with the current, i.e. 1.1.22+)
let Ok(old_config) = ConfigV1_1_32::read_from_default_path(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 mixnode is using <= v1.1.32 config template.");
info!("It is going to get updated to the current specification.");

let updated: Config = old_config.into();
updated
.save_to_default_location()
.map_err(|err| MixnodeError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})?;

Ok(true)
}

pub(crate) fn try_upgrade_config(id: &str) -> Result<(), MixnodeError> {
if try_upgrade_v1_1_21_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_32_config(id)? {
return Ok(());
}
Ok(())
}
62 changes: 52 additions & 10 deletions mixnode/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use crate::config::persistence::paths::MixNodePaths;
use crate::config::template::CONFIG_TEMPLATE;
use log::{debug, warn};
use nym_bin_common::logging::LoggingSettings;
use nym_config::defaults::{
mainnet, DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT,
Expand All @@ -16,13 +17,14 @@ use nym_config::{
use nym_node::config;
use serde::{Deserialize, Serialize};
use std::io;
use std::net::IpAddr;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::time::Duration;
use url::Url;

pub(crate) mod old_config_v1_1_21;
pub(crate) mod old_config_v1_1_32;
pub mod persistence;
mod template;

Expand Down Expand Up @@ -71,13 +73,30 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
.join(DEFAULT_DATA_DIR)
}

fn default_mixnode_http_config() -> config::Http {
config::Http {
bind_address: SocketAddr::new(
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
DEFAULT_HTTP_API_LISTENING_PORT,
),
landing_page_assets_path: None,
}
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub mixnode: MixNode,
// additional metadata holding on-disk location of this config file
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,

pub host: config::Host,

#[serde(default = "default_mixnode_http_config")]
pub http: config::Http,

pub mixnode: MixNode,

pub storage_paths: MixNodePaths,

#[serde(default)]
Expand All @@ -101,11 +120,13 @@ impl Config {
let default_mixnode = MixNode::new_default(id.as_ref());

Config {
save_path: None,
host: config::Host {
// this is a very bad default!
public_ips: vec![default_mixnode.listening_address],
hostname: None,
},
http: default_mixnode_http_config(),
mixnode: default_mixnode,
storage_paths: MixNodePaths::new_default(id.as_ref()),
verloc: Default::default(),
Expand All @@ -114,12 +135,24 @@ impl Config {
}
}

// 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)
}

// currently this is dead code, but once we allow loading configs from custom paths
// well, we will have to be using it
#[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 {
Expand All @@ -131,6 +164,15 @@ impl Config {
save_formatted_config_to_file(self, config_save_location)
}

pub fn try_save(&self) -> io::Result<()> {
if let Some(save_location) = &self.save_path {
save_formatted_config_to_file(self, save_location)
} else {
warn!("config file save location is unknown. falling back to the default");
self.save_to_default_location()
}
}

// builder methods
pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec<Url>) -> Self {
self.mixnode.nym_api_urls = nym_api_urls;
Expand All @@ -139,6 +181,10 @@ impl Config {

pub fn with_listening_address(mut self, listening_address: IpAddr) -> Self {
self.mixnode.listening_address = listening_address;

let http_port = self.http.bind_address.port();
self.http.bind_address = SocketAddr::new(listening_address, http_port);

self
}

Expand All @@ -153,7 +199,8 @@ impl Config {
}

pub fn with_http_api_port(mut self, port: u16) -> Self {
self.mixnode.http_api_port = port;
let http_ip = self.http.bind_address.ip();
self.http.bind_address = SocketAddr::new(http_ip, port);
self
}

Expand Down Expand Up @@ -181,10 +228,6 @@ pub struct MixNode {
/// (default: 1790)
pub verloc_port: u16,

/// Port used for listening for http requests.
/// (default: 8000)
pub http_api_port: u16,

/// Addresses to nym APIs from which the node gets the view of the network.
pub nym_api_urls: Vec<Url>,
}
Expand All @@ -197,7 +240,6 @@ impl MixNode {
listening_address: inaddr_any(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
nym_api_urls: vec![Url::from_str(mainnet::NYM_API).expect("Invalid default API URL")],
}
}
Expand Down
25 changes: 10 additions & 15 deletions mixnode/src/config/old_config_v1_1_21.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright 2023 - Nym Technologies SA <[email protected]>
// SPDX-License-Identifier: Apache-2.0

use crate::config::old_config_v1_1_32::{
ConfigV1_1_32, DebugV1_1_32, MixNodeV1_1_32, VerlocV1_1_32,
};
use crate::config::persistence::paths::{KeysPaths, MixNodePaths};
use crate::config::{Config, Debug, MixNode, Verloc};
use nym_bin_common::logging::LoggingSettings;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_validator_client::nyxd;
Expand Down Expand Up @@ -67,13 +69,13 @@ pub struct ConfigV1_1_21 {
debug: DebugV1_1_21,
}

impl From<ConfigV1_1_21> for Config {
impl From<ConfigV1_1_21> for ConfigV1_1_32 {
fn from(value: ConfigV1_1_21) -> Self {
let node_description =
ConfigV1_1_21::default_config_directory(&value.mixnode.id).join(DESCRIPTION_FILE);

Config {
mixnode: MixNode {
ConfigV1_1_32 {
mixnode: MixNodeV1_1_32 {
version: value.mixnode.version,
id: value.mixnode.id,
listening_address: value.mixnode.listening_address,
Expand All @@ -82,13 +84,6 @@ impl From<ConfigV1_1_21> for Config {
http_api_port: value.mixnode.http_api_port,
nym_api_urls: value.mixnode.nym_api_urls,
},
// \/ ADDED
host: nym_node::config::Host {
// this is a very bad default!
public_ips: vec![value.mixnode.listening_address],
hostname: None,
},
// /\ ADDED
storage_paths: MixNodePaths {
keys: KeysPaths {
private_identity_key_file: value.mixnode.private_identity_key_file,
Expand Down Expand Up @@ -176,9 +171,9 @@ struct VerlocV1_1_21 {
retry_timeout: Duration,
}

impl From<VerlocV1_1_21> for Verloc {
impl From<VerlocV1_1_21> for VerlocV1_1_32 {
fn from(value: VerlocV1_1_21) -> Self {
Verloc {
VerlocV1_1_32 {
packets_per_node: value.packets_per_node,
connection_timeout: value.connection_timeout,
packet_timeout: value.packet_timeout,
Expand Down Expand Up @@ -227,9 +222,9 @@ struct DebugV1_1_21 {
use_legacy_framed_packet_version: bool,
}

impl From<DebugV1_1_21> for Debug {
impl From<DebugV1_1_21> for DebugV1_1_32 {
fn from(value: DebugV1_1_21) -> Self {
Debug {
DebugV1_1_32 {
node_stats_logging_delay: value.node_stats_logging_delay,
node_stats_updating_delay: value.node_stats_updating_delay,
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
Expand Down
Loading

0 comments on commit ccd8b3f

Please sign in to comment.