From a2fc3e53fd75758db507024239756e466d13b3d9 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 15 Feb 2021 14:12:52 +0100 Subject: [PATCH] Display parachain validators (#4667) --- .../src/Overview/Parachain.tsx | 29 ++++++++++++++++--- .../src/Overview/ParachainList.tsx | 27 +++++++++++++++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/packages/page-parachains/src/Overview/Parachain.tsx b/packages/page-parachains/src/Overview/Parachain.tsx index 6de0676d27..10ddbc54c8 100644 --- a/packages/page-parachains/src/Overview/Parachain.tsx +++ b/packages/page-parachains/src/Overview/Parachain.tsx @@ -2,17 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import type { Option, Vec } from '@polkadot/types'; -import type { BlockNumber, HeadData, Header, ParaId } from '@polkadot/types/interfaces'; +import type { AccountId, BlockNumber, HeadData, Header, ParaId } from '@polkadot/types/interfaces'; import type { Codec } from '@polkadot/types/types'; import BN from 'bn.js'; -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; -import { Badge, ParaLink } from '@polkadot/react-components'; +import { AddressMini, Badge, Expander, ParaLink } from '@polkadot/react-components'; import { useApi, useCall, useCallMulti, useParaApi } from '@polkadot/react-hooks'; import { BlockToTime } from '@polkadot/react-query'; import { BN_ONE, formatNumber } from '@polkadot/util'; +import { useTranslation } from '../translate'; import { sliceHex } from '../util'; interface Props { @@ -22,6 +23,7 @@ interface Props { isScheduled?: boolean; lastBacked?: [string, string, BN]; lastInclusion?: [string, string, BN]; + validators?: AccountId[]; } type QueryResult = [Option, Option, Vec, Vec, Vec, Vec, Option]; @@ -63,7 +65,8 @@ const optionsMulti = { }) }; -function Parachain ({ bestNumber, className = '', id, isScheduled, lastBacked, lastInclusion }: Props): React.ReactElement { +function Parachain ({ bestNumber, className = '', id, isScheduled, lastBacked, lastInclusion, validators }: Props): React.ReactElement { + const { t } = useTranslation(); const { api } = useApi(); const { api: paraApi } = useParaApi(id); const paraBest = useCall(paraApi?.rpc.chain.subscribeNewHeads, undefined, transformHeader); @@ -89,6 +92,16 @@ function Parachain ({ bestNumber, className = '', id, isScheduled, lastBacked, l [bestNumber, lastRelayNumber, paraInfo] ); + const valRender = useCallback( + () => validators?.map((id) => ( + + )), + [validators] + ); + return (

{formatNumber(id)}

@@ -99,6 +112,14 @@ function Parachain ({ bestNumber, className = '', id, isScheduled, lastBacked, l /> )} + + {validators && validators.length !== 0 && ( + ('Validators ({{count}})', { replace: { count: formatNumber(validators.length) } })} + /> + )} + {paraInfo.headHex} {blockDelay && } diff --git a/packages/page-parachains/src/Overview/ParachainList.tsx b/packages/page-parachains/src/Overview/ParachainList.tsx index 77282c3b67..66f4194ece 100644 --- a/packages/page-parachains/src/Overview/ParachainList.tsx +++ b/packages/page-parachains/src/Overview/ParachainList.tsx @@ -2,14 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import type { SignedBlockExtended } from '@polkadot/api-derive/type'; -import type { CandidateReceipt, Event, ParaId } from '@polkadot/types/interfaces'; +import type { AccountId, CandidateReceipt, Event, ParaId, ParaValidatorIndex } from '@polkadot/types/interfaces'; import type { ScheduledProposals } from '../types'; import BN from 'bn.js'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { Table } from '@polkadot/react-components'; -import { useApi, useCall } from '@polkadot/react-hooks'; +import { useApi, useCall, useCallMulti } from '@polkadot/react-hooks'; import { useTranslation } from '../translate'; import Parachain from './Parachain'; @@ -26,6 +26,10 @@ interface LastEvents { lastIncluded: EventMap; } +const optionsMulti = { + defaultValue: [null, null] as [AccountId[] | null, ParaValidatorIndex[][] | null] +}; + function includeEntry (map: EventMap, event: Event, blockHash: string, blockNumber: BN): void { const { descriptor } = event.data[0] as CandidateReceipt; @@ -44,6 +48,10 @@ function ParachainList ({ ids, scheduled }: Props): React.ReactElement { const bestNumber = useCall(api.derive.chain.bestNumber); const lastBlock = useCall(api.derive.chain.subscribeNewBlocks); const [{ lastBacked, lastIncluded }, setLastEvents] = useState({ lastBacked: {}, lastIncluded: {} }); + const [validators, validatorGroups] = useCallMulti<[AccountId[] | null, ParaValidatorIndex[][] | null]>([ + api.query.session.validators, + api.query.paraScheduler?.validatorGroups || api.query.scheduler?.validatorGroups + ], optionsMulti); const scheduledIds = useMemo( () => (scheduled || []).reduce((all: Record, { scheduledIds }: ScheduledProposals): Record => { @@ -52,6 +60,17 @@ function ParachainList ({ ids, scheduled }: Props): React.ReactElement { [scheduled] ); + const validatorMap = useMemo( + () => validators && validatorGroups && ids && ids.length === validatorGroups.length + ? validatorGroups.map((ids) => + ids + .map((id) => validators[id.toNumber()]) + .filter((a) => a) + ) + : [], + [ids, validators, validatorGroups] + ); + useEffect((): void => { lastBlock && setLastEvents((prev) => { const backed: Record = {}; @@ -88,6 +107,7 @@ function ParachainList ({ ids, scheduled }: Props): React.ReactElement { const headerRef = useRef([ [t('parachains'), 'start', 3], + ['', 'media--1500'], [t('heads'), 'start'], [t('included (parent)'), undefined, 2], [t('backed')], @@ -101,7 +121,7 @@ function ParachainList ({ ids, scheduled }: Props): React.ReactElement { empty={ids && t('There are no registered parachains')} header={headerRef.current} > - {ids?.map((id): React.ReactNode => ( + {ids?.map((id, index): React.ReactNode => ( { key={id.toString()} lastBacked={lastBacked[id.toString()]} lastInclusion={lastIncluded[id.toString()]} + validators={validatorMap[index]} /> ))}