Display routing scores for bonded gateway (#1693)
* access gateway report from node status api * Create 4 response types for gateway and mixnode uptime and status * Add the three remaining validator-client functions * display gatways routing scores * handle undefined gateway report Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
This commit is contained in:
@@ -10,7 +10,6 @@ pub const GATEWAYS: &str = "gateways";
|
||||
pub const DETAILED: &str = "detailed";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
|
||||
pub const COCONUT_ROUTES: &str = "coconut";
|
||||
pub const BANDWIDTH: &str = "bandwidth";
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ fn main() {
|
||||
validator_api::status::mixnode_reward_estimation,
|
||||
validator_api::status::mixnode_stake_saturation,
|
||||
validator_api::status::mixnode_status,
|
||||
validator_api::status::gateway_report,
|
||||
vesting::rewards::vesting_claim_delegator_reward,
|
||||
vesting::rewards::vesting_claim_operator_reward,
|
||||
vesting::bond::vesting_bond_gateway,
|
||||
|
||||
@@ -6,8 +6,9 @@ use crate::error::BackendError;
|
||||
use crate::state::WalletState;
|
||||
use mixnet_contract_common::{IdentityKeyRef, MixId};
|
||||
use validator_client::models::{
|
||||
GatewayCoreStatusResponse, InclusionProbabilityResponse, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse,
|
||||
GatewayCoreStatusResponse, GatewayStatusReportResponse, InclusionProbabilityResponse,
|
||||
MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
@@ -32,6 +33,14 @@ pub async fn gateway_core_node_status(
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn gateway_report(
|
||||
identity: IdentityKeyRef<'_>,
|
||||
state: tauri::State<'_, WalletState>,
|
||||
) -> Result<GatewayStatusReportResponse, BackendError> {
|
||||
Ok(api_client!(state).get_gateway_report(identity).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn mixnode_status(
|
||||
mix_id: MixId,
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Stack, Typography } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { TBondedGateway, urls } from 'src/context';
|
||||
import { NymCard } from 'src/components';
|
||||
import { Network } from 'src/types';
|
||||
import { IdentityKey } from 'src/components/IdentityKey';
|
||||
import { getGatewayReport } from 'src/requests';
|
||||
import { Cell, Header, NodeTable } from './NodeTable';
|
||||
import { BondedGatewayActions, TBondedGatwayActions } from './BondedGatewayAction';
|
||||
import { Console } from 'src/utils/console';
|
||||
|
||||
const headers: Header[] = [
|
||||
{
|
||||
header: 'IP',
|
||||
id: 'ip',
|
||||
sx: { pl: 0 },
|
||||
},
|
||||
{
|
||||
header: 'Bond',
|
||||
id: 'bond',
|
||||
},
|
||||
|
||||
{
|
||||
header: 'Routing score',
|
||||
id: 'routing-score',
|
||||
tooltipText: 'Routing score',
|
||||
},
|
||||
{
|
||||
header: 'Average score',
|
||||
id: 'average-score',
|
||||
tooltipText: 'Average score',
|
||||
},
|
||||
{
|
||||
header: 'IP',
|
||||
id: 'ip',
|
||||
},
|
||||
{
|
||||
id: 'menu-button',
|
||||
sx: { width: 34, maxWidth: 34 },
|
||||
},
|
||||
];
|
||||
|
||||
@@ -33,18 +44,26 @@ export const BondedGateway = ({
|
||||
network?: Network;
|
||||
onActionSelect: (action: TBondedGatwayActions) => void;
|
||||
}) => {
|
||||
const { name, bond, ip, identityKey } = gateway;
|
||||
const { name, bond, ip, identityKey, routingScore } = gateway;
|
||||
const cells: Cell[] = [
|
||||
{
|
||||
cell: ip,
|
||||
id: 'ip-cell',
|
||||
},
|
||||
{
|
||||
cell: `${bond.amount} ${bond.denom}`,
|
||||
id: 'bond-cell',
|
||||
sx: { pl: 0 },
|
||||
},
|
||||
|
||||
{
|
||||
cell: `${routingScore?.current || '- '}%`,
|
||||
id: 'routing-score-cell',
|
||||
},
|
||||
{
|
||||
cell: `${routingScore?.average || '- '}%`,
|
||||
id: 'average-score-cell',
|
||||
},
|
||||
{
|
||||
cell: ip,
|
||||
id: 'ip-cell',
|
||||
},
|
||||
{
|
||||
cell: <BondedGatewayActions onActionSelect={onActionSelect} />,
|
||||
id: 'actions-cell',
|
||||
@@ -73,7 +92,7 @@ export const BondedGateway = ({
|
||||
<NodeTable headers={headers} cells={cells} />
|
||||
{network && (
|
||||
<Typography sx={{ mt: 2, fontSize: 'small' }}>
|
||||
Check more stats of your node on the{' '}
|
||||
Check more stats of your gateway on the{' '}
|
||||
<Link href={`${urls(network).networkExplorer}/network-components/gateways`} target="_blank">
|
||||
explorer
|
||||
</Link>
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
getInclusionProbability,
|
||||
getMixnodeAvgUptime,
|
||||
getMixnodeRewardEstimation,
|
||||
getGatewayReport,
|
||||
} from '../requests';
|
||||
import { useCheckOwnership } from '../hooks/useCheckOwnership';
|
||||
import { AppContext } from './main';
|
||||
@@ -78,6 +79,10 @@ export interface TBondedGateway {
|
||||
mixPort: number;
|
||||
verlocPort: number;
|
||||
version: string;
|
||||
routingScore?: {
|
||||
current: number;
|
||||
average: number;
|
||||
};
|
||||
}
|
||||
|
||||
export type TokenPool = 'locked' | 'balance';
|
||||
@@ -215,6 +220,16 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
return stake;
|
||||
};
|
||||
|
||||
const getGatewayReportDetails = async (identityKey: string) => {
|
||||
try {
|
||||
const report = await getGatewayReport(identityKey);
|
||||
return { current: report.most_recent, average: report.last_day };
|
||||
} catch (e) {
|
||||
Console.error(e);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
@@ -282,7 +297,7 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
const data = await getGatewayBondDetails();
|
||||
if (data) {
|
||||
const nodeDescription = await getNodeDescription(data.gateway.host, data.gateway.clients_port);
|
||||
|
||||
const routingScore = await getGatewayReportDetails(data.gateway.identity_key);
|
||||
setBondedNode({
|
||||
name: nodeDescription?.name,
|
||||
identityKey: data.gateway.identity_key,
|
||||
@@ -290,6 +305,7 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
location: data.gateway.location,
|
||||
bond: data.pledge_amount,
|
||||
proxy: data.proxy,
|
||||
routingScore,
|
||||
} as TBondedGateway);
|
||||
}
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -38,6 +38,10 @@ const bondedGatewayMock: TBondedGateway = {
|
||||
mixPort: 1789,
|
||||
verlocPort: 1790,
|
||||
version: '1.0.2',
|
||||
routingScore: {
|
||||
average: 100,
|
||||
current: 100,
|
||||
},
|
||||
};
|
||||
|
||||
const TxResultMock: TransactionExecuteResult = {
|
||||
|
||||
@@ -75,7 +75,7 @@ export const NodeSettings = () => {
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<NodeIcon />
|
||||
<Typography variant="h6" fontWeight={600}>
|
||||
Node Settings
|
||||
{isMixnode(bondedNode) ? 'Node' : 'Gateway'} Settings
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -3,8 +3,10 @@ import { Box, Button, Typography, Grid, TextField } from '@mui/material';
|
||||
import { TBondedMixnode, TBondedGateway } from 'src/context/bonding';
|
||||
import { Error } from 'src/components/Error';
|
||||
import { UnbondModal } from 'src/components/Bonding/modals/UnbondModal';
|
||||
import { isMixnode } from 'src/types';
|
||||
interface Props {
|
||||
bondedNode: TBondedMixnode | TBondedGateway;
|
||||
|
||||
onConfirm: () => Promise<void>;
|
||||
onError: (e: string) => void;
|
||||
}
|
||||
@@ -21,15 +23,21 @@ export const NodeUnbondPage = ({ bondedNode, onConfirm, onError }: Props) => {
|
||||
Unbond
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="body2" sx={{ color: (theme) => theme.palette.nym.text.muted }}>
|
||||
If you unbond your node you will loose all delegations!
|
||||
</Typography>
|
||||
</Grid>
|
||||
{isMixnode(bondedNode) && (
|
||||
<Grid item>
|
||||
<Typography variant="body2" sx={{ color: (theme) => theme.palette.nym.text.muted }}>
|
||||
If you unbond you will loose all delegations!
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item container direction={'column'} spacing={2} width={0.5} padding={3}>
|
||||
<Grid item>
|
||||
<Error message="Unbonding is irreversible and it won’t be possible to restore the current state of your node again" />
|
||||
<Error
|
||||
message={`Unbonding is irreversible and it won’t be possible to restore the current state of your ${
|
||||
isMixnode(bondedNode) ? 'node' : 'gateway'
|
||||
} again`}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="body2">
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
WrappedDelegationEvent,
|
||||
PendingIntervalEvent,
|
||||
} from '@nymproject/types';
|
||||
import { Interval, TNodeDescription } from 'src/types';
|
||||
import { Interval, TGatewayReport, TNodeDescription } from 'src/types';
|
||||
import { invokeWrapper } from './wrapper';
|
||||
|
||||
export const getAllPendingDelegations = async () =>
|
||||
@@ -48,3 +48,6 @@ export const getNodeDescription = async (host: string, port: number) =>
|
||||
|
||||
export const getPendingIntervalEvents = async () =>
|
||||
invokeWrapper<PendingIntervalEvent[]>('get_pending_interval_events');
|
||||
|
||||
export const getGatewayReport = async (identity: string) =>
|
||||
invokeWrapper<TGatewayReport>('gateway_report', { identity });
|
||||
|
||||
@@ -61,6 +61,14 @@ export type TAccount = {
|
||||
mnemonic: string;
|
||||
};
|
||||
|
||||
export type TGatewayReport = {
|
||||
identity: string;
|
||||
owner: string;
|
||||
last_day: number;
|
||||
last_hour: number;
|
||||
most_recent: number;
|
||||
};
|
||||
|
||||
export const isMixnode = (node: TBondedMixnode | TBondedGateway): node is TBondedMixnode =>
|
||||
(node as TBondedMixnode).profitMargin !== undefined;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user