From e975051566caf46a9fd882e70bc67babcb05cb4d Mon Sep 17 00:00:00 2001 From: dynco-nym <173912580+dynco-nym@users.noreply.github.com> Date: Thu, 5 Jun 2025 20:22:52 +0200 Subject: [PATCH] Filter by semver --- .../nym-node-status-api/src/http/api/dvpn.rs | 19 ++++++++--- .../nym-node-status-api/src/http/state.rs | 32 ++++++++++++++----- .../nym-node-status-api/src/monitor/mod.rs | 9 +++--- .../src/node_scraper/mod.rs | 2 +- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/nym-node-status-api/nym-node-status-api/src/http/api/dvpn.rs b/nym-node-status-api/nym-node-status-api/src/http/api/dvpn.rs index 13b73c3dcd..26dc516f40 100644 --- a/nym-node-status-api/nym-node-status-api/src/http/api/dvpn.rs +++ b/nym-node-status-api/nym-node-status-api/src/http/api/dvpn.rs @@ -2,7 +2,9 @@ use axum::{ extract::{Path, Query, State}, Json, Router, }; +use semver::Version; use serde::Deserialize; +use std::sync::LazyLock; use tracing::instrument; use utoipa::IntoParams; @@ -12,6 +14,8 @@ use crate::http::{ state::AppState, }; +static MIN_SUPPORTED_VERSION: LazyLock = LazyLock::new(|| Version::new(1, 6, 2)); + pub(crate) fn routes() -> Router { Router::new() .route("/", axum::routing::get(dvpn_gateways)) @@ -45,12 +49,17 @@ async fn dvpn_gateways( Query(MinNodeVersionQuery { min_node_version }): Query, state: State, ) -> HttpResult>> { - let min_node_version: String = min_node_version.unwrap_or_else(|| String::from("1.6.2")); - let _min_node_version = semver::Version::parse(&min_node_version) - .map_err(|_| HttpError::invalid_input("Min version must be valid semver"))?; + let min_node_version = match min_node_version { + Some(min_version) => semver::Version::parse(&min_version) + .map_err(|_| HttpError::invalid_input("Min version must be valid semver"))?, + None => MIN_SUPPORTED_VERSION.clone(), + }; Ok(Json( - state.cache().get_dvpn_gateway_list(state.db_pool()).await, + state + .cache() + .get_dvpn_gateway_list(state.db_pool(), &min_node_version) + .await, )) } @@ -87,7 +96,7 @@ async fn gateways_by_country( 2 => Ok(Json( state .cache() - .get_dvpn_gateway_list(state.db_pool()) + .get_dvpn_gateway_list(state.db_pool(), &MIN_SUPPORTED_VERSION) .await .into_iter() .filter(|gw| { diff --git a/nym-node-status-api/nym-node-status-api/src/http/state.rs b/nym-node-status-api/nym-node-status-api/src/http/state.rs index 55338d8782..4c947199e8 100644 --- a/nym-node-status-api/nym-node-status-api/src/http/state.rs +++ b/nym-node-status-api/nym-node-status-api/src/http/state.rs @@ -5,6 +5,7 @@ use nym_contracts_common::NaiveFloat; use nym_crypto::asymmetric::ed25519::PublicKey; use nym_mixnet_contract_common::NodeId; use nym_validator_client::nym_api::SkimmedNode; +use semver::Version; use std::{collections::HashMap, sync::Arc, time::Duration}; use tokio::sync::RwLock; use tracing::{error, instrument, warn}; @@ -80,7 +81,6 @@ impl AppState { } static GATEWAYS_LIST_KEY: &str = "gateways"; -static DVPN_GATEWAYS_LIST_KEY: &str = "dvpn_gateways"; static MIXNODES_LIST_KEY: &str = "mixnodes"; static NYM_NODES_LIST_KEY: &str = "nym_nodes"; static MIXSTATS_LIST_KEY: &str = "mixstats"; @@ -92,7 +92,8 @@ const MIXNODE_STATS_HISTORY_DAYS: usize = 30; #[derive(Debug, Clone)] pub(crate) struct HttpCache { gateways: Cache>>>, - dvpn_gateways: Cache>>>, + // each min_version has their own cached list + dvpn_gateways: Cache>>>, mixnodes: Cache>>>, nym_nodes: Cache>>>, mixstats: Cache>>>, @@ -108,7 +109,7 @@ impl HttpCache { .time_to_live(Duration::from_secs(ttl_seconds)) .build(), dvpn_gateways: Cache::builder() - .max_capacity(2) + .max_capacity(6) .time_to_live(Duration::from_secs(ttl_seconds)) .build(), mixnodes: Cache::builder() @@ -182,9 +183,10 @@ impl HttpCache { pub async fn upsert_dvpn_gateway_list( &self, new_gateway_list: Vec, - ) -> Entry>>> { + min_node_version: &Version, + ) -> Entry>>> { self.dvpn_gateways - .entry_by_ref(DVPN_GATEWAYS_LIST_KEY) + .entry_by_ref(min_node_version) .and_upsert_with(|maybe_entry| async { if let Some(entry) = maybe_entry { let v = entry.into_value(); @@ -198,8 +200,12 @@ impl HttpCache { .await } - pub async fn get_dvpn_gateway_list(&self, db: &DbPool) -> Vec { - match self.dvpn_gateways.get(DVPN_GATEWAYS_LIST_KEY).await { + pub async fn get_dvpn_gateway_list( + &self, + db: &DbPool, + min_node_version: &Version, + ) -> Vec { + match self.dvpn_gateways.get(min_node_version).await { Some(guard) => { tracing::trace!("Fetching from cache..."); let read_lock = guard.read().await; @@ -258,6 +264,15 @@ impl HttpCache { } }, ) + .filter(|gw| { + let gw_version = &gw.build_information.build_version; + if let Ok(gw_version) = Version::parse(gw_version) { + &gw_version >= min_node_version + } else { + warn!("Failed to parse GW version {}", gw_version); + false + } + }) .filter(|gw| { // gateways must have a country if gw.location.two_letter_iso_country_code.len() == 2 { @@ -282,7 +297,8 @@ impl HttpCache { if res_gws.is_empty() && started_with > 0 { tracing::warn!("Started with {}, got 0 gateways", started_with); } else { - self.upsert_dvpn_gateway_list(res_gws.clone()).await; + self.upsert_dvpn_gateway_list(res_gws.clone(), min_node_version) + .await; } res_gws diff --git a/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs b/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs index 9a5dc2ceaa..7461837fe0 100644 --- a/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs +++ b/nym-node-status-api/nym-node-status-api/src/monitor/mod.rs @@ -105,11 +105,10 @@ impl Monitor { .clone() .expect("rust sdk mainnet default missing api_url"); - let nym_api = - nym_http_api_client::ClientBuilder::new_with_url(default_api_url.into()) - .no_hickory_dns() - .with_timeout(self.nym_api_client_timeout) - .build::<&str>()?; + let nym_api = nym_http_api_client::ClientBuilder::new_with_url(default_api_url) + .no_hickory_dns() + .with_timeout(self.nym_api_client_timeout) + .build::<&str>()?; let api_client = NymApiClient::from(nym_api); diff --git a/nym-node-status-api/nym-node-status-api/src/node_scraper/mod.rs b/nym-node-status-api/nym-node-status-api/src/node_scraper/mod.rs index c3cfebb1b2..aeab6cb4b9 100644 --- a/nym-node-status-api/nym-node-status-api/src/node_scraper/mod.rs +++ b/nym-node-status-api/nym-node-status-api/src/node_scraper/mod.rs @@ -57,7 +57,7 @@ async fn run( .clone() .expect("rust sdk mainnet default missing api_url"); - let nym_api = nym_http_api_client::ClientBuilder::new_with_url(default_api_url.into()) + let nym_api = nym_http_api_client::ClientBuilder::new_with_url(default_api_url) .no_hickory_dns() .with_timeout(nym_api_client_timeout) .build::<&str>()?;