/gateways works

- SkimmedNode data still missing
- need to move probe models to monorepo
This commit is contained in:
dynco-nym
2025-05-27 16:45:41 +02:00
parent 3e5537b753
commit ea08345025
6 changed files with 138 additions and 78 deletions
@@ -3,7 +3,7 @@
set -eu
export ENVIRONMENT=${ENVIRONMENT:-"mainnet"}
probe_git_ref="nym-vpn-core-v1.4.0"
probe_git_ref="nym-vpn-core-v1.9.0"
crate_root=$(dirname $(realpath "$0"))
monorepo_root=$(realpath "${crate_root}/../..")
@@ -8,6 +8,7 @@ use utoipa_swagger_ui::SwaggerUi;
use crate::http::{server::HttpServer, state::AppState};
pub(crate) mod dvpn;
pub(crate) mod gateways;
pub(crate) mod metrics;
pub(crate) mod mixnodes;
@@ -15,7 +16,6 @@ pub(crate) mod nym_nodes;
pub(crate) mod services;
pub(crate) mod summary;
pub(crate) mod testruns;
pub(crate) mod dvpn;
pub(crate) struct RouterBuilder {
unfinished_router: Router<AppState>,
@@ -1,12 +1,20 @@
use cosmwasm_std::{Addr, Coin, Decimal};
use nym_mixnet_contract_common::CoinSchema;
use nym_node_requests::api::v1::node::models::NodeDescription;
use nym_validator_client::client::NodeId;
use nym_validator_client::{
client::NodeId,
models::{
AuthenticatorDetails, BinaryBuildInformationOwned, IpPacketRouterDetails, NymNodeData,
},
nym_nodes::{BasicEntryInformation, NodeRole},
};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
pub(crate) use nym_node_status_client::models::TestrunAssignment;
use crate::monitor::ExplorerPrettyBond;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct Gateway {
pub gateway_identity_key: String,
@@ -23,25 +31,6 @@ pub struct Gateway {
pub config_score: u32,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub enum Role {
// a properly active mixnode
Mixnode {
layer: u8,
},
#[serde(alias = "entry", alias = "gateway")]
EntryGateway,
#[serde(alias = "exit")]
ExitGateway,
// equivalent of node that's in rewarded set but not in the inactive set
Standby,
Inactive,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct BuildInformation {
pub build_version: String,
@@ -49,23 +38,6 @@ pub struct BuildInformation {
pub commit_sha: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct IpPacketRouter {
pub address: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct Authenticator {
pub address: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct EntryInformation {
pub hostname: Option<String>,
pub ws_port: u16,
pub wss_port: Option<u16>,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct Location {
pub two_letter_iso_country_code: String,
@@ -77,39 +49,131 @@ pub struct Location {
pub struct DVpnGateway {
pub identity_key: String,
pub name: String,
pub ip_packet_router: Option<IpPacketRouter>,
pub authenticator: Option<Authenticator>,
pub ip_packet_router: Option<IpPacketRouterDetails>,
pub authenticator: Option<AuthenticatorDetails>,
pub location: Location,
pub last_probe: Option<serde_json::Value>,
pub last_probe: Option<DirectoryGwProbeOutcome>,
pub ip_addresses: Vec<String>,
pub mix_port: u16,
pub role: Role,
pub entry: EntryInformation,
pub role: NodeRole,
pub entry: Option<BasicEntryInformation>,
// The performance data here originates from the nym-api, and is effectively mixnet performance
// at the time of writing this
pub performance: u8,
pub build_information: Option<BuildInformation>,
pub build_information: BinaryBuildInformationOwned,
}
impl TryFrom<Gateway> for DVpnGateway {
type Error = ();
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct LastProbeResult {
node: String,
used_entry: String,
outcome: DirectoryGwProbeOutcome,
}
fn try_from(value: Gateway) -> Result<Self, Self::Error> {
Self::try_from(&value)
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct DirectoryGwProbe {
last_updated_utc: String,
outcome: DirectoryGwProbeOutcome,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct DirectoryGwProbeOutcome {
as_entry: directory_gw_probe_outcome::AsEntry,
as_exit: directory_gw_probe_outcome::AsExit,
wg: directory_gw_probe_outcome::Wg,
}
pub mod directory_gw_probe_outcome {
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct AsEntry {
can_connect: bool,
can_route: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct AsExit {
can_connect: bool,
can_route_ip_v4: bool,
can_route_ip_external_v4: bool,
can_route_ip_v6: bool,
can_route_ip_external_v6: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct Wg {
wg: wg_outcome_versions::DirectoryGatewayProbeOutcomeWGv2,
}
}
impl TryFrom<&Gateway> for DVpnGateway
{
type Error = ();
pub mod wg_outcome_versions {
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct DirectoryGatewayProbeOutcomeWGv1 {
can_handshake: bool,
can_register: bool,
can_resolve_dns: bool,
ping_hosts_performance: f64,
ping_ips_performance: f64,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct DirectoryGatewayProbeOutcomeWGv2 {
pub can_register: bool,
pub can_handshake_v4: bool,
pub can_resolve_dns_v4: bool,
pub ping_hosts_performance_v4: f32,
pub ping_ips_performance_v4: f32,
pub can_handshake_v6: bool,
pub can_resolve_dns_v6: bool,
pub ping_hosts_performance_v6: f32,
pub ping_ips_performance_v6: f32,
pub download_duration_sec_v4: u64,
pub downloaded_file_v4: String,
pub download_error_v4: String,
pub download_duration_sec_v6: u64,
pub downloaded_file_v6: String,
pub download_error_v6: String,
}
}
impl TryFrom<&Gateway> for DVpnGateway {
type Error = anyhow::Error;
fn try_from(value: &Gateway) -> Result<Self, Self::Error> {
Self::try_from(value.to_owned())
}
}
// TODO: try to parse out values from gateway and fail when unable to parse
impl TryFrom<Gateway> for DVpnGateway {
type Error = anyhow::Error;
fn try_from(value: Gateway) -> Result<Self, Self::Error> {
let location = value
.explorer_pretty_bond
.ok_or_else(|| anyhow::anyhow!("Missing explorer_pretty_bond"))
.and_then(|value| {
serde_json::from_value::<ExplorerPrettyBond>(value).map_err(From::from)
})
.map(|bond| bond.location)?;
let self_described = value
.self_described
.ok_or_else(|| anyhow::anyhow!("Missing self_described"))
.and_then(|value| serde_json::from_value::<NymNodeData>(value).map_err(From::from))?;
let last_probe_result = value
.last_probe_result
.and_then(|value| serde_json::from_value::<LastProbeResult>(value).ok());
// TODO: polyfill `last_probe`, see below from VPN API
/**
/*
const last_testrun_utc = item.last_testrun_utc;
const last_probe: DirectoryGatewayProbe | undefined =
@@ -141,26 +205,23 @@ impl TryFrom<&Gateway> for DVpnGateway
*/
Ok(Self {
identity_key: value.gateway_identity_key.clone(),
name: value.description.moniker.clone(),
ip_packet_router: None, // value.ip_packet_router,
authenticator: None, // value.authenticator,
identity_key: value.gateway_identity_key,
name: value.description.moniker,
ip_packet_router: self_described.ip_packet_router,
authenticator: self_described.authenticator,
location: Location {
latitude: 0.0f64,
longitude: 0.0f64,
two_letter_iso_country_code: "".to_string(),
latitude: location.location.latitude,
longitude: location.location.longitude,
two_letter_iso_country_code: location.two_letter_iso_country_code,
},
last_probe: value.last_probe_result.clone(),
ip_addresses: vec![], // value.ip_addresses,
mix_port: 0u16, // value.mix_port,
role: Role::EntryGateway, // value.role,
entry: EntryInformation {
ws_port: 0u16,
wss_port: None,
hostname: None,
},
performance: value.performance,
build_information: None, // value.build_information,
last_probe: last_probe_result.map(|res| res.outcome),
// TODO
ip_addresses: vec![], // value.ip_addresses (SkimmedNode),
mix_port: 0, // value.mix_port (SkimmedNode),
role: NodeRole::EntryGateway, // value.role (SkimmedNode),
entry: None, // (SkimmedNode)
performance: value.performance, // (SkimmedNode)
build_information: self_described.build_information,
})
}
}
@@ -63,7 +63,7 @@ impl IpInfoClient {
}
}
#[derive(Debug, Clone, Serialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ExplorerPrettyBond {
pub(crate) identity_key: String,
pub(crate) owner: Addr,
@@ -71,7 +71,7 @@ pub(crate) struct ExplorerPrettyBond {
pub(crate) location: Location,
}
#[derive(Debug, Clone, Default, Serialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub(crate) struct Location {
pub(crate) two_letter_iso_country_code: String,
#[serde(flatten)]
@@ -7,7 +7,6 @@ use crate::db::models::{
NYMNODES_DESCRIBED_COUNT, NYMNODE_COUNT,
};
use crate::db::{queries, DbPool};
use crate::monitor::geodata::{ExplorerPrettyBond, Location};
use crate::utils::now_utc;
use crate::utils::{decimal_to_i64, LogError, NumericalCheckedCast};
use anyhow::anyhow;
@@ -30,7 +29,7 @@ use std::{
use tokio::{sync::RwLock, time::Duration};
use tracing::instrument;
pub(crate) use geodata::IpInfoClient;
pub(crate) use geodata::{ExplorerPrettyBond, IpInfoClient, Location};
pub(crate) use node_delegations::DelegationsCache;
mod geodata;
@@ -24,7 +24,7 @@ pub(crate) async fn spawn(pool: DbPool, refresh_interval: Duration) {
});
}
#[instrument(level = "debug", name = "testrun_queue", skip_all)]
#[instrument(level = "info", name = "testrun_queue", skip_all)]
async fn run(pool: &DbPool) -> anyhow::Result<()> {
tracing::info!("Spawning testruns...");
if pool.is_closed() {