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:
Fouad
2022-10-27 10:34:11 +01:00
committed by GitHub
parent 4990a4745f
commit d36e349cc6
10 changed files with 92 additions and 25 deletions
@@ -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";
+1
View File
@@ -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>
+17 -1
View File
@@ -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) {
+4
View File
@@ -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 wont be possible to restore the current state of your node again" />
<Error
message={`Unbonding is irreversible and it wont be possible to restore the current state of your ${
isMixnode(bondedNode) ? 'node' : 'gateway'
} again`}
/>
</Grid>
<Grid item>
<Typography variant="body2">
+4 -1
View File
@@ -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 });
+8
View File
@@ -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;