diff --git a/Cargo.lock b/Cargo.lock index 3a9d8e0676..43cfaed711 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6326,6 +6326,8 @@ dependencies = [ "regex", "schemars 0.8.22", "serde", + "serde_json", + "tracing", "url", "utoipa", ] diff --git a/common/network-defaults/Cargo.toml b/common/network-defaults/Cargo.toml index d8cbc36711..1287d2a7e4 100644 --- a/common/network-defaults/Cargo.toml +++ b/common/network-defaults/Cargo.toml @@ -12,6 +12,8 @@ dotenvy = { workspace = true, optional = true } log = { workspace = true, optional = true } schemars = { workspace = true, features = ["preserve_order"], optional = true } serde = { workspace = true, features = ["derive"], optional = true } +serde_json = {workspace = true, optional = true } +tracing = {workspace = true, optional = true } url = { workspace = true, optional = true } utoipa = { workspace = true, optional = true } @@ -20,7 +22,7 @@ utoipa = { workspace = true, optional = true } [features] default = ["env", "network"] -env = ["dotenvy", "log"] +env = ["dotenvy", "log", "serde_json", "tracing"] network = ["schemars", "serde", "url"] utoipa = [ "dep:utoipa" ] diff --git a/common/network-defaults/src/mainnet.rs b/common/network-defaults/src/mainnet.rs index 8e253a77b7..4465875b36 100644 --- a/common/network-defaults/src/mainnet.rs +++ b/common/network-defaults/src/mainnet.rs @@ -72,6 +72,13 @@ pub const NYM_VPN_APIS: &[ApiUrlConst] = &[ }, ]; +#[cfg(feature = "env")] +fn serialize_api_urls(urls: &[ApiUrlConst]) -> String { + serde_json::to_string(urls) + .inspect_err(|e| tracing::warn!("failed to serialize nym_api_urls for env: {e}")) + .unwrap_or_default() +} + // I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging pub const EXIT_POLICY_URL: &str = "https://nymtech.net/.wellknown/network-requester/exit-policy.txt"; @@ -162,9 +169,11 @@ pub fn export_to_env() { ); set_var_to_default(var_names::NYXD, NYXD_URL); set_var_to_default(var_names::NYM_API, NYM_API); + set_var_to_default(var_names::NYM_APIS, &serialize_api_urls(NYM_APIS)); set_var_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS); set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL); set_var_to_default(var_names::NYM_VPN_API, NYM_VPN_API); + set_var_to_default(var_names::NYM_VPN_APIS, &serialize_api_urls(NYM_VPN_APIS)); set_var_to_default( var_names::UPGRADE_MODE_ATTESTATION_URL, UPGRADE_MODE_ATTESTATION_URL, @@ -211,6 +220,9 @@ pub fn export_to_env_if_not_set() { ); set_var_conditionally_to_default(var_names::NYXD, NYXD_URL); set_var_conditionally_to_default(var_names::NYM_API, NYM_API); + set_var_conditionally_to_default(var_names::NYM_APIS, &serialize_api_urls(NYM_APIS)); + set_var_conditionally_to_default(var_names::NYM_VPN_API, NYM_VPN_API); + set_var_conditionally_to_default(var_names::NYM_VPN_APIS, &serialize_api_urls(NYM_VPN_APIS)); set_var_conditionally_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS); set_var_conditionally_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL); set_var_conditionally_to_default( diff --git a/common/network-defaults/src/network.rs b/common/network-defaults/src/network.rs index 23ca16dddf..c85101b690 100644 --- a/common/network-defaults/src/network.rs +++ b/common/network-defaults/src/network.rs @@ -7,6 +7,13 @@ use serde::{Deserialize, Serialize}; use std::ops::Not; use url::Url; +#[cfg(feature = "env")] +use crate::var_names; +#[cfg(feature = "env")] +use std::env::{VarError, var}; +#[cfg(feature = "env")] +use std::ffi::OsStr; + #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] pub struct ChainDetails { @@ -55,7 +62,7 @@ pub struct ApiUrl { pub front_hosts: Option>, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, Serialize)] pub struct ApiUrlConst<'a> { pub url: &'a str, pub front_hosts: Option<&'a [&'a str]>, @@ -106,10 +113,6 @@ impl NymNetworkDetails { #[cfg(feature = "env")] pub fn new_from_env() -> Self { - use crate::var_names; - use std::env::{VarError, var}; - use std::ffi::OsStr; - fn get_optional_env>(env: K) -> Option { match var(env) { Ok(var) => { @@ -125,6 +128,11 @@ impl NymNetworkDetails { } let nym_api = var(var_names::NYM_API).expect("nym api not set"); + let nym_api_urls = try_parse_api_urls(var_names::NYM_APIS).unwrap_or(vec![ApiUrl { + url: nym_api.clone(), + front_hosts: None, + }]); + let nym_vpn_api_urls = try_parse_api_urls(var_names::NYM_VPN_APIS); NymNetworkDetails::new_empty() .with_network_name(var(var_names::NETWORK_NAME).expect("network name not set")) @@ -161,10 +169,8 @@ impl NymNetworkDetails { .with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS)) .with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS)) .with_nym_vpn_api_url(get_optional_env(var_names::NYM_VPN_API)) - .with_nym_api_urls(Some(vec![ApiUrl { - url: nym_api, - front_hosts: None, - }])) + .with_nym_vpn_api_urls(nym_vpn_api_urls) + .with_nym_api_urls(nym_api_urls) } pub fn new_mainnet() -> Self { @@ -218,6 +224,9 @@ impl NymNetworkDetails { } } unsafe { + let nym_api_urls = self.nym_api_urls(); + let nym_vpn_api_urls = self.nym_vpn_api_urls(); + set_var(var_names::NETWORK_NAME, self.network_name); set_var(var_names::BECH32_PREFIX, self.chain_details.bech32_account_prefix); @@ -243,9 +252,10 @@ impl NymNetworkDetails { set_optional_var(var_names::COCONUT_DKG_CONTRACT_ADDRESS, self.contracts.coconut_dkg_contract_address); set_optional_var(var_names::NYM_VPN_API, self.nym_vpn_api_url); + set_optional_var(var_names::NYM_VPN_APIS, nym_vpn_api_urls); + set_optional_var(var_names::NYM_APIS, nym_api_urls); + } - - } pub fn default_gas_price_amount(&self) -> f64 { @@ -355,8 +365,14 @@ impl NymNetworkDetails { } #[must_use] - pub fn with_nym_api_urls(mut self, urls: Option>) -> Self { - self.nym_api_urls = urls; + pub fn with_nym_api_urls(mut self, urls: Vec) -> Self { + self.nym_api_urls = Some(urls); + self + } + + #[must_use] + pub fn with_nym_vpn_api_urls(mut self, urls: Option>) -> Self { + self.nym_vpn_api_urls = urls; self } @@ -366,6 +382,28 @@ impl NymNetworkDetails { .expect("the provided nym-vpn api url is invalid!") }) } + + #[cfg(feature = "env")] + fn nym_api_urls(&self) -> Option { + serde_json::to_string(self.nym_api_urls.as_deref()?) + .inspect_err(|e| tracing::warn!("failed to serialize nym_api_urls for env: {e}")) + .ok() + } + + #[cfg(feature = "env")] + fn nym_vpn_api_urls(&self) -> Option { + serde_json::to_string(self.nym_vpn_api_urls.as_deref()?) + .inspect_err(|e| tracing::warn!("failed to serialize nym_vpn_api_urls for env: {e}")) + .ok() + } +} + +#[cfg(feature = "env")] +fn try_parse_api_urls(k: impl AsRef) -> Option> { + let raw = var(k).ok()?; + serde_json::from_str(&raw) + .inspect_err(|e| tracing::warn!("failed to parse api urls from env \"{raw:?}\": {e}")) + .ok() } #[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)] diff --git a/common/network-defaults/src/var_names.rs b/common/network-defaults/src/var_names.rs index 897937b0fe..4bfb1d5877 100644 --- a/common/network-defaults/src/var_names.rs +++ b/common/network-defaults/src/var_names.rs @@ -21,9 +21,11 @@ pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS"; pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS"; pub const NYXD: &str = "NYXD"; pub const NYM_API: &str = "NYM_API"; +pub const NYM_APIS: &str = "NYM_APIS"; pub const NYXD_WEBSOCKET: &str = "NYXD_WS"; pub const EXIT_POLICY_URL: &str = "EXIT_POLICY"; pub const NYM_VPN_API: &str = "NYM_VPN_API"; +pub const NYM_VPN_APIS: &str = "NYM_VPN_APIS"; pub const CLIENT_STATS_COLLECTION_PROVIDER: &str = "CLIENT_STATS_COLLECTION_PROVIDER"; pub const UPGRADE_MODE_ATTESTATION_URL: &str = "UPGRADE_MODE_ATTESTATION_URL"; pub const UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY: &str = "UPGRADE_MODE_ATTESTER_ED25519_PUBKEY";