diff --git a/api/api.js b/api/api.js index 4b51dc10..d33467c9 100644 --- a/api/api.js +++ b/api/api.js @@ -26,7 +26,7 @@ import {FriendlyGet} from './friendlyGet'; import config from './config'; import {addNetworkExplorerRoutes} from './network-explorer'; import {FULLNODE_URL} from './fullnode-url'; -import {lamportsToSol} from './util'; +import {calculateUptimeValues, lamportsToSol} from './util'; const GLOBAL_STATS_BROADCAST_INTERVAL_MS = 2000; const CLUSTER_INFO_BROADCAST_INTERVAL_MS = 5000; @@ -579,6 +579,8 @@ async function getClusterInfo() { feeCalculator, inflation, currentSlot, + epochInfo, + epochSchedule, supply, clusterNodes, leader, @@ -589,6 +591,8 @@ async function getClusterInfo() { .with('feeCalculator', connection.getRecentBlockhash()) .with('inflation', connection.getInflation()) .with('currentSlot', getAsync('!blk-last-slot')) + .with('epochInfo', connection.getEpochInfo()) + .with('epochSchedule', connection.getEpochSchedule()) .with('supply', connection.getTotalSupply()) .with('clusterNodes', connection.getClusterNodes(), []) .with('leader', connection.getSlotLeader()) @@ -695,6 +699,11 @@ async function getClusterInfo() { node.votePubkey = votePubkey; node.identity = _.find(identities, x => x.pubkey === nodePubkey); node.uptime = _.find(uptime, x => x.nodePubkey === nodePubkey); + node.uptimeStats = calculateUptimeValues( + epochInfo, + epochSchedule, + node.uptime && node.uptime.uptime, + ); node.voteStatus = _.find(voteAccounts.current, x => x.nodePubkey === nodePubkey) || diff --git a/src/v2/components/Validators/Detail/index.jsx b/src/v2/components/Validators/Detail/index.jsx index cdf1b7c1..fb439cf9 100644 --- a/src/v2/components/Validators/Detail/index.jsx +++ b/src/v2/components/Validators/Detail/index.jsx @@ -15,6 +15,7 @@ import ValidatorsMap from 'v2/components/ValidatorsMap'; import InfoRow from 'v2/components/InfoRow'; import useStyles from './styles'; +import Uptime from '../../UI/Uptime'; const ValidatorsDetail = ({match}: {match: Match}) => { const {validators, inactiveValidators} = NodesStore; @@ -42,9 +43,16 @@ const ValidatorsDetail = ({match}: {match: Match}) => { identity = {}, stakedSol, stakedSolPercent, - calcUptime, + uptimeStats, } = node; + const { + lastEpochUptimePercent, + cumulativeUptimePercent, + uptimeEpochs, + uptimeComplete, + } = uptimeStats || {}; + const specs = [ { label: 'Address', @@ -73,7 +81,14 @@ const ValidatorsDetail = ({match}: {match: Match}) => { { label: 'Uptime', hint: '', - value: `${calcUptime}%`, + value: ( + + ), }, { label: 'keybase', diff --git a/src/v2/components/Validators/Table/index.jsx b/src/v2/components/Validators/Table/index.jsx index 505499ed..b3d5dd50 100644 --- a/src/v2/components/Validators/Table/index.jsx +++ b/src/v2/components/Validators/Table/index.jsx @@ -23,6 +23,7 @@ import ValidatorName from 'v2/components/UI/ValidatorName'; import TableCard from 'v2/components/UI/TableCard'; import useStyles from './styles'; +import Uptime from '../../UI/Uptime'; const fields = [ { @@ -76,8 +77,14 @@ const ValidatorsTable = ({separate}: {separate: boolean}) => { stakedSol, stakedSolPercent, calcCommission, - calcUptime, + uptimeStats, } = row; + const { + lastEpochUptimePercent, + cumulativeUptimePercent, + uptimeEpochs, + uptimeComplete, + } = uptimeStats || {}; return ( @@ -91,7 +98,14 @@ const ValidatorsTable = ({separate}: {separate: boolean}) => {
{stakedSol ? `${stakedSol} (${stakedSolPercent}%)` : 'N/A'}
{calcCommission ? `${calcCommission}%` : 'N/A'} - {calcUptime ? `${calcUptime}%` : 'Unavailable'} + + +
); }; @@ -102,8 +116,14 @@ const ValidatorsTable = ({separate}: {separate: boolean}) => { stakedSol, stakedSolPercent, calcCommission, - calcUptime, + uptimeStats, } = card; + const { + lastEpochUptimePercent, + cumulativeUptimePercent, + uptimeEpochs, + uptimeComplete, + } = uptimeStats || {}; const data = [ { label: 'Name', @@ -125,7 +145,14 @@ const ValidatorsTable = ({separate}: {separate: boolean}) => { }, { label: 'Uptime', - value: calcUptime ? `${calcUptime}%` : 'Unavailable', + value: ( + + ), }, ]; return ; diff --git a/src/v2/components/ValidatorsMap/Marker.jsx b/src/v2/components/ValidatorsMap/Marker.jsx index 8b0e4264..3e000b84 100644 --- a/src/v2/components/ValidatorsMap/Marker.jsx +++ b/src/v2/components/ValidatorsMap/Marker.jsx @@ -5,6 +5,7 @@ import Avatar from 'v2/components/UI/Avatar'; import ValidatorName from 'v2/components//UI/ValidatorName'; import useStyles from './styles'; +import Uptime from '../UI/Uptime'; const Marker = ({scale, marker}: {scale: number, marker: any}) => { const classes = useStyles(); @@ -13,11 +14,16 @@ const Marker = ({scale, marker}: {scale: number, marker: any}) => { nodePubkey, identity = {}, calcCommission, - calcUptime, + uptimeStats, stakedSol, stakedSolPercent, } = marker; - + const { + lastEpochUptimePercent, + cumulativeUptimePercent, + uptimeEpochs, + uptimeComplete, + } = uptimeStats || {}; return ( {
Commission: {calcCommission ? `${calcCommission}%` : 'N/A'}
-
Uptime: {calcUptime ? `${calcUptime}%` : 'Unavailable'}
+
+ {' '} + Uptime:{' '} + +
)} diff --git a/src/v2/stores/nodes.js b/src/v2/stores/nodes.js index 4a80d5ce..b98a5721 100644 --- a/src/v2/stores/nodes.js +++ b/src/v2/stores/nodes.js @@ -1,12 +1,6 @@ -import {filter, reject, map} from 'lodash/fp'; +import {filter, reject} from 'lodash/fp'; import {action, computed, decorate, observable, flow} from 'mobx'; import * as API from 'v2/api/stats'; -import getUptime from 'v2/utils/getUptime'; - -const addNetworkSolInfo = () => node => ({ - ...node, - calcUptime: getUptime(node.uptime), -}); class Store { network = []; @@ -15,9 +9,7 @@ class Store { if (typeof data === 'string') { data = JSON.parse(data); } - this.network = data.network - ? map(addNetworkSolInfo(data.totalStaked))(data.network) - : []; + this.network = data.network || []; this.totalStaked = data.totalStaked; this.totalStakedSol = data.totalStakedSol; this.supply = data.supply; diff --git a/src/v2/utils/getUptime.js b/src/v2/utils/getUptime.js deleted file mode 100644 index 502c61f0..00000000 --- a/src/v2/utils/getUptime.js +++ /dev/null @@ -1,13 +0,0 @@ -import {compose, getOr, multiply} from 'lodash/fp'; - -export default compose( - // - // FIXME: this masks possible calculation errors, see: - // - // https://github.com/solana-labs/networkexplorer/issues/456 - // https://github.com/solana-labs/networkexplorer/issues/425 - // - time => (time > 100 ? 100 : parseFloat(time.toFixed(time ? 4 : 2))), - multiply(100), - getOr(0, 'uptime.uptime.[0].percentage'), -);