Files
nym/nym-node-status-api/src/db/models.rs
T
Dinko Zdravac cc983963d4 Fully functional network scores (#5048)
* Compile & copy wg probe

* Node status agent WIP

* Enable debug logging

* Agent submits results
- add clap to agent
- agent runs network probe
- /submit endpoint on NS API

* Build clients with timeouts

* Update logging and dev scripts

* Replace /blaclisted endpoint

* Testruns fully functional
- task that queues testruns periodically
- testruns read/write in DB

* Probe scores fully working
- testruns are assigned on API
- submit updates testruns correctly on NS API side
- agent registers with API
- agent submits results correctly

* Clippy fixes

* PR feedback

* Clippy again

* PR feedback

* Run clippy earlier in CI

* Make refresh delay configurable in server & agent
2024-10-28 17:31:43 +01:00

335 lines
11 KiB
Rust

use crate::{
http::{self, models::SummaryHistory},
monitor::NumericalCheckedCast,
};
use nym_node_requests::api::v1::node::models::NodeDescription;
use serde::{Deserialize, Serialize};
use strum_macros::{EnumString, FromRepr};
use utoipa::ToSchema;
pub(crate) struct GatewayRecord {
pub(crate) identity_key: String,
pub(crate) bonded: bool,
pub(crate) blacklisted: bool,
pub(crate) self_described: Option<String>,
pub(crate) explorer_pretty_bond: Option<String>,
pub(crate) last_updated_utc: i64,
pub(crate) performance: u8,
}
#[derive(Debug, Clone)]
pub(crate) struct GatewayDto {
pub(crate) gateway_identity_key: String,
pub(crate) bonded: bool,
pub(crate) blacklisted: bool,
pub(crate) performance: i64,
pub(crate) self_described: Option<String>,
pub(crate) explorer_pretty_bond: Option<String>,
pub(crate) last_probe_result: Option<String>,
pub(crate) last_probe_log: Option<String>,
pub(crate) last_testrun_utc: Option<i64>,
pub(crate) last_updated_utc: i64,
pub(crate) moniker: String,
pub(crate) security_contact: String,
pub(crate) details: String,
pub(crate) website: String,
}
impl TryFrom<GatewayDto> for http::models::Gateway {
type Error = anyhow::Error;
fn try_from(value: GatewayDto) -> Result<Self, Self::Error> {
// Instead of using routing_score_successes / routing_score_samples, we use the
// number of successful testruns in the last 24h.
let routing_score = 0f32;
let config_score = 0u32;
let last_updated_utc =
timestamp_as_utc(value.last_updated_utc.cast_checked()?).to_rfc3339();
let last_testrun_utc = value
.last_testrun_utc
.and_then(|i| i.cast_checked().ok())
.map(|t| timestamp_as_utc(t).to_rfc3339());
let self_described = value.self_described.clone().unwrap_or("null".to_string());
let explorer_pretty_bond = value
.explorer_pretty_bond
.clone()
.unwrap_or("null".to_string());
let last_probe_result = value
.last_probe_result
.clone()
.unwrap_or("null".to_string());
let last_probe_log = value.last_probe_log.clone();
let self_described = serde_json::from_str(&self_described).unwrap_or(None);
let explorer_pretty_bond = serde_json::from_str(&explorer_pretty_bond).unwrap_or(None);
let last_probe_result = serde_json::from_str(&last_probe_result).unwrap_or(None);
let bonded = value.bonded;
let blacklisted = value.blacklisted;
let performance = value.performance as u8;
let description = NodeDescription {
moniker: value.moniker.clone(),
website: value.website.clone(),
security_contact: value.security_contact.clone(),
details: value.details.clone(),
};
Ok(http::models::Gateway {
gateway_identity_key: value.gateway_identity_key.clone(),
bonded,
blacklisted,
performance,
self_described,
explorer_pretty_bond,
description,
last_probe_result,
last_probe_log,
routing_score,
config_score,
last_testrun_utc,
last_updated_utc,
})
}
}
fn timestamp_as_utc(unix_timestamp: u64) -> chrono::DateTime<chrono::Utc> {
let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(unix_timestamp);
d.into()
}
pub(crate) struct MixnodeRecord {
pub(crate) mix_id: u32,
pub(crate) identity_key: String,
pub(crate) bonded: bool,
pub(crate) total_stake: i64,
pub(crate) host: String,
pub(crate) http_port: u16,
pub(crate) blacklisted: bool,
pub(crate) full_details: String,
pub(crate) self_described: Option<String>,
pub(crate) last_updated_utc: i64,
pub(crate) is_dp_delegatee: bool,
}
#[derive(Debug, Clone)]
pub(crate) struct MixnodeDto {
pub(crate) mix_id: i64,
pub(crate) bonded: bool,
pub(crate) blacklisted: bool,
pub(crate) is_dp_delegatee: bool,
pub(crate) total_stake: i64,
pub(crate) full_details: String,
pub(crate) self_described: Option<String>,
pub(crate) last_updated_utc: i64,
pub(crate) moniker: String,
pub(crate) website: String,
pub(crate) security_contact: String,
pub(crate) details: String,
}
impl TryFrom<MixnodeDto> for http::models::Mixnode {
type Error = anyhow::Error;
fn try_from(value: MixnodeDto) -> Result<Self, Self::Error> {
let mix_id = value.mix_id.cast_checked()?;
let full_details = value.full_details.clone();
let full_details = serde_json::from_str(&full_details).unwrap_or(None);
let self_described = value
.self_described
.clone()
.map(|v| serde_json::from_str(&v).unwrap_or(serde_json::Value::Null));
let last_updated_utc =
timestamp_as_utc(value.last_updated_utc.cast_checked()?).to_rfc3339();
let blacklisted = value.blacklisted;
let is_dp_delegatee = value.is_dp_delegatee;
let moniker = value.moniker.clone();
let website = value.website.clone();
let security_contact = value.security_contact.clone();
let details = value.details.clone();
Ok(http::models::Mixnode {
mix_id,
bonded: value.bonded,
blacklisted,
is_dp_delegatee,
total_stake: value.total_stake,
full_details,
description: NodeDescription {
moniker,
website,
security_contact,
details,
},
self_described,
last_updated_utc,
})
}
}
#[allow(unused)]
#[derive(Debug, Clone)]
pub(crate) struct BondedStatusDto {
pub(crate) id: i64,
pub(crate) identity_key: String,
pub(crate) bonded: bool,
}
#[allow(unused)]
#[derive(Debug, Clone, Default)]
pub(crate) struct SummaryDto {
pub(crate) key: String,
pub(crate) value_json: String,
pub(crate) last_updated_utc: i64,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct SummaryHistoryDto {
#[allow(dead_code)]
pub id: i64,
pub date: String,
pub value_json: String,
pub timestamp_utc: i64,
}
impl TryFrom<SummaryHistoryDto> for SummaryHistory {
type Error = anyhow::Error;
fn try_from(value: SummaryHistoryDto) -> Result<Self, Self::Error> {
let value_json = serde_json::from_str(&value.value_json).unwrap_or_default();
Ok(SummaryHistory {
value_json,
date: value.date.clone(),
timestamp_utc: timestamp_as_utc(value.timestamp_utc.cast_checked()?).to_rfc3339(),
})
}
}
pub(crate) const MIXNODES_BONDED_COUNT: &str = "mixnodes.bonded.count";
pub(crate) const MIXNODES_BONDED_ACTIVE: &str = "mixnodes.bonded.active";
pub(crate) const MIXNODES_BONDED_INACTIVE: &str = "mixnodes.bonded.inactive";
pub(crate) const MIXNODES_BONDED_RESERVE: &str = "mixnodes.bonded.reserve";
pub(crate) const MIXNODES_BLACKLISTED_COUNT: &str = "mixnodes.blacklisted.count";
pub(crate) const GATEWAYS_BONDED_COUNT: &str = "gateways.bonded.count";
pub(crate) const GATEWAYS_EXPLORER_COUNT: &str = "gateways.explorer.count";
pub(crate) const GATEWAYS_BLACKLISTED_COUNT: &str = "gateways.blacklisted.count";
pub(crate) const MIXNODES_HISTORICAL_COUNT: &str = "mixnodes.historical.count";
pub(crate) const GATEWAYS_HISTORICAL_COUNT: &str = "gateways.historical.count";
// `utoipa`` goes crazy if you use module-qualified prefix as field type so we
// have to import it
use gateway::GatewaySummary;
use mixnode::MixnodeSummary;
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct NetworkSummary {
pub(crate) mixnodes: MixnodeSummary,
pub(crate) gateways: GatewaySummary,
}
pub(crate) mod mixnode {
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct MixnodeSummary {
pub(crate) bonded: MixnodeSummaryBonded,
pub(crate) blacklisted: MixnodeSummaryBlacklisted,
pub(crate) historical: MixnodeSummaryHistorical,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct MixnodeSummaryBonded {
pub(crate) count: i32,
pub(crate) active: i32,
pub(crate) inactive: i32,
pub(crate) reserve: i32,
pub(crate) last_updated_utc: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct MixnodeSummaryBlacklisted {
pub(crate) count: i32,
pub(crate) last_updated_utc: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct MixnodeSummaryHistorical {
pub(crate) count: i32,
pub(crate) last_updated_utc: String,
}
}
pub(crate) mod gateway {
use super::*;
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct GatewaySummary {
pub(crate) bonded: GatewaySummaryBonded,
pub(crate) blacklisted: GatewaySummaryBlacklisted,
pub(crate) historical: GatewaySummaryHistorical,
pub(crate) explorer: GatewaySummaryExplorer,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct GatewaySummaryExplorer {
pub(crate) count: i32,
pub(crate) last_updated_utc: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct GatewaySummaryBonded {
pub(crate) count: i32,
pub(crate) last_updated_utc: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct GatewaySummaryHistorical {
pub(crate) count: i32,
pub(crate) last_updated_utc: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub(crate) struct GatewaySummaryBlacklisted {
pub(crate) count: i32,
pub(crate) last_updated_utc: String,
}
}
#[derive(Debug, Clone)]
pub struct TestRunDto {
pub id: i64,
pub gateway_id: i64,
pub status: i64,
pub timestamp_utc: i64,
pub ip_address: String,
pub log: String,
}
#[derive(Debug, Clone, strum_macros::Display, EnumString, FromRepr, PartialEq)]
#[repr(u8)]
pub(crate) enum TestRunStatus {
Complete = 2,
InProgress = 1,
Pending = 0,
}
#[derive(Debug, Clone)]
pub struct GatewayIdentityDto {
pub gateway_identity_key: String,
pub bonded: bool,
}
#[allow(dead_code)] // it's not dead code but clippy doesn't detect usage in sqlx macros
#[derive(Debug, Clone)]
pub struct GatewayInfoDto {
pub id: i64,
pub gateway_identity_key: String,
pub self_described: Option<String>,
pub explorer_pretty_bond: Option<String>,
}