Add config score for gateway

This commit is contained in:
Yana
2025-01-10 16:48:38 +02:00
parent 50494ea1e5
commit 88c7009e88
3 changed files with 280 additions and 48 deletions
@@ -35,6 +35,8 @@ export default async function NymNode({
const observatoryNymNodes: IObservatoryNode[] =
await observatoryResponse.json();
console.log("observatoryNymNodes :>> ", observatoryNymNodes);
if (!observatoryNymNodes) {
return null;
}
@@ -69,20 +71,22 @@ export default async function NymNode({
<Grid size={12}>
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
<SectionHeading title="Nym Node Details" />
<ExplorerButtonGroup
options={[
{
label: "Nym Node",
isSelected: true,
link: `/nym-node/${id}`,
},
{
label: "Account",
isSelected: false,
link: `/account/${observatoryNymNode.bonding_address}`,
},
]}
/>
{observatoryNymNode.bonding_address && (
<ExplorerButtonGroup
options={[
{
label: "Nym Node",
isSelected: true,
link: `/nym-node/${id}`,
},
{
label: "Account",
isSelected: false,
link: `/account/${observatoryNymNode.bonding_address}`,
},
]}
/>
)}
</Box>
</Grid>
{observatoryNymNode && (
@@ -95,7 +99,7 @@ export default async function NymNode({
<NodeProfileCard nodeInfo={observatoryNymNode} />
</Grid>
)}
{observatoryNymNode && (
{observatoryNymNode.rewarding_details && (
<Grid
size={{
xs: 12,
@@ -118,17 +122,19 @@ export default async function NymNode({
<QualityIndicatorsCard nodeInfo={observatoryNymNode} />
</Grid>
)}
<Grid
size={{
xs: 12,
md: 6,
}}
>
<NodeRewardsCard
rewardDetails={observatoryNymNode.rewarding_details}
nodeInfo={observatoryNymNode}
/>
</Grid>
{observatoryNymNode.rewarding_details && (
<Grid
size={{
xs: 12,
md: 6,
}}
>
<NodeRewardsCard
rewardDetails={observatoryNymNode.rewarding_details}
nodeInfo={observatoryNymNode}
/>
</Grid>
)}
{observatoryNymNode && (
<Grid
size={{
+111
View File
@@ -254,3 +254,114 @@ export interface NodeRewardDetails {
node_id: number;
owner: string;
}
export type LastProbeResult = {
gateway: string;
outcome: {
as_entry: {
can_connect: boolean;
can_route: boolean;
};
as_exit: {
can_connect: boolean;
can_route_ip_external_v4: boolean;
can_route_ip_external_v6: boolean;
can_route_ip_v4: boolean;
can_route_ip_v6: boolean;
};
wg: {
can_handshake_v4: boolean;
can_handshake_v6: boolean;
can_register: boolean;
can_resolve_dns_v4: boolean;
can_resolve_dns_v6: boolean;
ping_hosts_performance_v4: number;
ping_hosts_performance_v6: number;
ping_ips_performance_v4: number;
ping_ips_performance_v6: number;
};
};
};
export type GatewayStatus = {
blacklisted: boolean;
bonded: boolean;
config_score: number;
description: {
details: string;
moniker: string;
security_contact: string;
website: string;
};
explorer_pretty_bond: {
identity_key: string;
location: {
latitude: number;
longitude: number;
two_letter_iso_country_code: string;
};
owner: string;
pledge_amount: {
amount: string;
denom: string;
};
};
gateway_identity_key: string;
last_probe_log: string;
last_probe_result: LastProbeResult; // Reference to the separate type
last_testrun_utc: string;
last_updated_utc: string;
performance: number;
routing_score: number;
self_described: {
authenticator: {
address: string;
};
auxiliary_details: {
accepted_operator_terms_and_conditions: boolean;
announce_ports: {
mix_port: number | null;
verloc_port: number | null;
};
location: string;
};
build_information: {
binary_name: string;
build_timestamp: string;
build_version: string;
cargo_profile: string;
cargo_triple: string;
};
declared_role: {
entry: boolean;
exit_ipr: boolean;
exit_nr: boolean;
mixnode: boolean;
};
host_information: {
hostname: string;
ip_address: string[];
keys: {
ed25519: string;
x25519: string;
x25519_noise: string | null;
};
};
ip_packet_router: {
address: string;
};
last_polled: string;
mixnet_websockets: {
ws_port: number;
wss_port: number | null;
};
network_requester: {
address: string;
uses_exit_policy: boolean;
};
wireguard: {
port: number;
public_key: string;
};
};
};
@@ -1,5 +1,13 @@
import type { IObservatoryNode, NodeDescription } from "@/app/api/types";
"use client";
import type {
GatewayStatus,
IObservatoryNode,
LastProbeResult,
NodeDescription,
} from "@/app/api/types";
import { Chip, Stack } from "@mui/material";
import { useEffect, useState } from "react";
import ExplorerCard from "../cards/ExplorerCard";
import ExplorerListItem from "../list/ListItem";
import StarRating from "../starRating/StarRating";
@@ -29,9 +37,103 @@ function getNodeRoles(
return activeRoles;
}
function calculateQualityOfServiceStars(quality: number): number {
if (quality < 0.3) {
return 1;
}
if (quality < 0.5) {
return 2;
}
if (quality < 0.7) {
return 3;
}
return 4;
}
function calculateConfigScoreStars(probeResult: LastProbeResult): number {
const { as_entry, as_exit } = probeResult.outcome;
if (as_entry && as_exit) {
// Combine all true/false values for as_entry and as_exit
const allResults = [
as_entry.can_connect,
as_entry.can_route,
as_exit.can_connect,
as_exit.can_route_ip_external_v4,
as_exit.can_route_ip_external_v6,
as_exit.can_route_ip_v4,
as_exit.can_route_ip_v6,
];
const combinedScore = allResults.filter(Boolean).length;
if (combinedScore === 7) {
return 4; // 4 stars if all 7 are true
}
if (combinedScore === 6) {
return 3; // 3 stars if 6 are true
}
if (combinedScore === 5) {
return 2; // 2 stars if 5 are true
}
return 1; // 1 star if less than 5 are true
}
// Check if only as_entry exists and calculate stars
if (as_entry) {
const { can_connect, can_route } = as_entry;
const entryScore = [can_connect, can_route].filter(Boolean).length;
if (entryScore === 2) {
return 4; // 4 stars if both are true
}
if (entryScore === 1) {
return 2; // 2 stars if one is true
}
return 1; // 1 star if both are false
}
// Check if only as_exit exists and calculate stars
if (as_exit) {
const {
can_connect,
can_route_ip_external_v4,
can_route_ip_external_v6,
can_route_ip_v4,
can_route_ip_v6,
} = as_exit;
const exitScore = [
can_connect,
can_route_ip_external_v4,
can_route_ip_external_v6,
can_route_ip_v4,
can_route_ip_v6,
].filter(Boolean).length;
if (exitScore === 5) {
return 4; // 4 stars if all 5 are true
}
if (exitScore === 4) {
return 3; // 3 stars if 4 true, 1 false
}
if (exitScore === 3) {
return 2; // 2 stars if 3 true, 2 false
}
return 1; // 1 star if 2 true or less
}
// Default case if neither as_entry nor as_exit is present
return 0; // No stars
}
export const QualityIndicatorsCard = (props: IQualityIndicatorsCardProps) => {
const { nodeInfo } = props;
const [gatewayProbeResult, setGatewayProbeResult] =
useState<LastProbeResult>();
const nodeRoles = getNodeRoles(nodeInfo.description.declared_role);
const NodeRoles = nodeRoles.map((role) => (
<Stack key={role} direction="row" gap={1}>
@@ -39,18 +141,6 @@ export const QualityIndicatorsCard = (props: IQualityIndicatorsCardProps) => {
</Stack>
));
function calculateQualityOfServiceStars(quality: number): number {
if (quality < 0.3) {
return 1;
}
if (quality < 0.5) {
return 2;
}
if (quality < 0.7) {
return 3;
}
return 4;
}
const qualityOfServiceStars = nodeInfo?.uptime
? calculateQualityOfServiceStars(nodeInfo?.uptime)
: 1;
@@ -58,6 +148,33 @@ export const QualityIndicatorsCard = (props: IQualityIndicatorsCardProps) => {
const nodeIsMixNodeOnly =
NodeRoles.length === 1 && nodeRoles[0] === "Mix Node";
useEffect(() => {
// Fetch data if the node has certain roles
if (
nodeRoles.includes("Entry Node") ||
nodeRoles.includes("Exit IPR Node") ||
nodeRoles.includes("Exit NR Node")
) {
const fetchData = async () => {
try {
const response = await fetch(
`https://mainnet-node-status-api.nymtech.cc/v2/gateways/${nodeInfo.identity_key}`,
);
const data: GatewayStatus = await response.json();
setGatewayProbeResult(data.last_probe_result);
} catch (error) {
console.error("Error fetching data:", error);
}
};
fetchData();
}
}, [nodeRoles, nodeInfo.identity_key]);
const configScoreStars = gatewayProbeResult
? calculateConfigScoreStars(gatewayProbeResult)
: 0;
return (
<ExplorerCard label="Quality indicatiors" sx={{ height: "100%" }}>
<ExplorerListItem
@@ -70,20 +187,18 @@ export const QualityIndicatorsCard = (props: IQualityIndicatorsCardProps) => {
</Stack>
}
/>
{nodeIsMixNodeOnly && (
<ExplorerListItem
row
divider
label="Quality of service"
value={<StarRating value={qualityOfServiceStars} />}
/>
)}
<ExplorerListItem
row
divider
label="Quality of service"
value={<StarRating value={qualityOfServiceStars} />}
/>
{!nodeIsMixNodeOnly && (
<ExplorerListItem
row
divider
label="Config score"
value={<StarRating value={4} />}
value={<StarRating value={configScoreStars} />}
/>
)}
{!nodeIsMixNodeOnly && (