Filter by semver

This commit is contained in:
dynco-nym
2025-06-05 20:22:52 +02:00
parent 8207a9c38a
commit e975051566
4 changed files with 43 additions and 19 deletions
@@ -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<Version> = LazyLock::new(|| Version::new(1, 6, 2));
pub(crate) fn routes() -> Router<AppState> {
Router::new()
.route("/", axum::routing::get(dvpn_gateways))
@@ -45,12 +49,17 @@ async fn dvpn_gateways(
Query(MinNodeVersionQuery { min_node_version }): Query<MinNodeVersionQuery>,
state: State<AppState>,
) -> HttpResult<Json<Vec<DVpnGateway>>> {
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| {
@@ -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<String, Arc<RwLock<Vec<Gateway>>>>,
dvpn_gateways: Cache<String, Arc<RwLock<Vec<DVpnGateway>>>>,
// each min_version has their own cached list
dvpn_gateways: Cache<Version, Arc<RwLock<Vec<DVpnGateway>>>>,
mixnodes: Cache<String, Arc<RwLock<Vec<Mixnode>>>>,
nym_nodes: Cache<String, Arc<RwLock<Vec<ExtendedNymNode>>>>,
mixstats: Cache<String, Arc<RwLock<Vec<DailyStats>>>>,
@@ -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<DVpnGateway>,
) -> Entry<String, Arc<RwLock<Vec<DVpnGateway>>>> {
min_node_version: &Version,
) -> Entry<Version, Arc<RwLock<Vec<DVpnGateway>>>> {
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<DVpnGateway> {
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<DVpnGateway> {
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
@@ -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);
@@ -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>()?;