Fix historic blocks produced display (#1966)

This commit is contained in:
Jaco Greeff 2019-11-30 13:11:37 +01:00 committed by GitHub
parent 05e2a668b0
commit 50e44f2f2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 40 deletions

View File

@ -3,19 +3,19 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { I18nProps } from '@polkadot/react-components/types';
import { Balance, Hash, Exposure, SessionIndex } from '@polkadot/types/interfaces';
import { SessionRewards, Slash } from '@polkadot/react-hooks/types';
import { Balance, Hash, Exposure } from '@polkadot/types/interfaces';
import { SessionRewards, Slash } from '../types';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import { Chart, Columar, Column } from '@polkadot/react-components';
import { toShortAddress } from '@polkadot/react-components/util';
import { getHistoric } from '@polkadot/react-api/util';
import { trackStream, useApi } from '@polkadot/react-hooks';
import { u32 } from '@polkadot/types';
import { useApi } from '@polkadot/react-hooks';
import { formatBalance, formatNumber } from '@polkadot/util';
import translate from '../translate';
import useBlockCounts from '../useBlockCounts';
interface Props extends I18nProps {
className?: string;
@ -92,11 +92,7 @@ function extractEraSlash (validatorId: string, slashes: Slash[]): BN {
function Validator ({ className, sessionRewards, t, validatorId }: Props): React.ReactElement<Props> {
const { api } = useApi();
// FIXME There is something seriously wrong in these two with "any" horrors
const blockCounts = trackStream<u32[]>(api.query.imOnline?.authoredBlocks?.multi as any, [sessionRewards, validatorId], {
paramMap: ([sessionRewards, validatorId]: [SessionRewards[], string]): any =>
[sessionRewards.map(({ sessionIndex }): [SessionIndex, string] => [sessionIndex, validatorId])]
});
const blockCounts = useBlockCounts(validatorId, sessionRewards);
const [blocksLabels, setBlocksLabels] = useState<string[]>([]);
const [blocksChart, setBlocksChart] = useState<LineData | null>(null);
const [{ currency, divisor }] = useState<{ currency: string; divisor: BN }>({

View File

@ -3,8 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { DerivedFees, DerivedBalances, DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/types';
import { BlockNumber } from '@polkadot/types/interfaces';
import { SessionRewards } from '@polkadot/react-hooks/types';
import { AccountId, Balance, BlockNumber, Hash, SessionIndex } from '@polkadot/types/interfaces';
export type Nominators = Record<string, string[]>;
@ -29,3 +28,19 @@ export interface CalculateBalanceProps {
export type AccountFilter = 'all' | 'controller' | 'session' | 'stash' | 'unbonded';
export type ValidatorFilter = 'all' | 'hasNominators' | 'noNominators' | 'hasWarnings' | 'noWarnings' | 'iNominated' | 'nextSet';
export interface Slash {
accountId: AccountId;
amount: Balance;
}
export interface SessionRewards {
blockHash: Hash;
blockNumber: BlockNumber;
isEventsEmpty: boolean;
parentHash: Hash;
reward: Balance;
sessionIndex: SessionIndex;
slashes: Slash[];
treasury: Balance;
}

View File

@ -0,0 +1,38 @@
// Copyright 2017-2019 @polkadot/app-staking authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { SessionIndex } from '@polkadot/types/interfaces';
import { SessionRewards } from './types';
import { useEffect, useState } from 'react';
import { useApi, trackStream } from '@polkadot/react-hooks';
import { u32 } from '@polkadot/types';
export default function useBlockCounts (accountId: string, sessionRewards: SessionRewards[]): u32[] {
const { api } = useApi();
const [counts, setCounts] = useState<u32[]>([]);
const [historic, setHistoric] = useState<u32[]>([]);
const sessionIndex = trackStream<SessionIndex>(api.query.session.currentIndex, []);
const current = trackStream<u32>(api.query.imOnline?.authoredBlocks, [sessionIndex, accountId]);
useEffect((): void => {
if (api.query.imOnline?.authoredBlocks && sessionRewards && sessionRewards.length) {
const filtered = sessionRewards.filter(({ sessionIndex }): boolean => sessionIndex.gtn(0));
if (filtered.length) {
Promise
.all(filtered.map(({ parentHash, sessionIndex }): Promise<u32> =>
api.query.imOnline.authoredBlocks.at(parentHash, sessionIndex.subn(1), accountId) as Promise<u32>
))
.then(setHistoric);
}
}
}, [accountId, sessionRewards]);
useEffect((): void => {
setCounts([...historic, current || api.createType('u32')]);
}, [current, historic]);
return counts;
}

View File

@ -3,7 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { Balance, EventRecord, Hash, Header, StorageChangeSet } from '@polkadot/types/interfaces';
import { Slash, SessionRewards } from '@polkadot/react-hooks/types';
import { Slash, SessionRewards } from './types';
import BN from 'bn.js';
import { useEffect, useState } from 'react';
@ -22,9 +22,11 @@ interface Serialized {
blockHash: string;
blockNumber: string;
isEventsEmpty: boolean;
parentHash: string;
reward: string;
sessionIndex: string;
slashes: SerializedSlash[];
treasury: string;
}
const MAX_BLOCKS = 2500;
@ -34,17 +36,20 @@ function fromJSON (sessions: Serialized[]): SessionRewards[] {
let keepAll = true;
return sessions
.map(({ blockHash, blockNumber, isEventsEmpty, reward, sessionIndex, slashes }): SessionRewards => ({
.map(({ blockHash, blockNumber, isEventsEmpty, parentHash, reward, sessionIndex, slashes, treasury }): SessionRewards => ({
blockHash: createType(registry, 'Hash', blockHash),
blockNumber: createType(registry, 'BlockNumber', blockNumber),
isEventsEmpty,
parentHash: createType(registry, 'Hash', parentHash),
reward: createType(registry, 'Balance', reward),
sessionIndex: createType(registry, 'SessionIndex', sessionIndex),
slashes: slashes.map(({ accountId, amount }): Slash => ({
accountId: createType(registry, 'AccountId', accountId),
amount: createType(registry, 'Balance', amount)
}))
})),
treasury: createType(registry, 'Balance', treasury)
}))
.filter(({ parentHash }): boolean => !parentHash.isEmpty)
.filter(({ isEventsEmpty }): boolean => {
if (!isEventsEmpty) {
// we first see if we have some data up to this point (we may not, i.e. non-archive)
@ -59,16 +64,18 @@ function fromJSON (sessions: Serialized[]): SessionRewards[] {
}
function toJSON (sessions: SessionRewards[], maxSessions: number): Serialized[] {
return sessions.map(({ blockHash, blockNumber, isEventsEmpty, reward, sessionIndex, slashes }): Serialized => ({
return sessions.map(({ blockHash, blockNumber, isEventsEmpty, parentHash, reward, sessionIndex, slashes, treasury }): Serialized => ({
blockHash: blockHash.toHex(),
blockNumber: blockNumber.toHex(),
isEventsEmpty,
parentHash: parentHash.toHex(),
reward: reward.toHex(),
sessionIndex: sessionIndex.toHex(),
slashes: slashes.map(({ accountId, amount }): SerializedSlash => ({
accountId: accountId.toString(),
amount: amount.toHex()
}))
})),
treasury: treasury.toHex()
})).slice(-maxSessions);
}
@ -108,10 +115,10 @@ async function loadSome (api: ApiPromise, fromHash: Hash, toHash: Hash): Promise
amount: amount as any
}))
);
const rewards: (Balance | undefined)[] = events.map((info): Balance | undefined => {
const rewards: [Balance | undefined, Balance | undefined][] = events.map((info): [Balance | undefined, Balance | undefined] => {
const rewards = info.filter(({ event: { method } }): boolean => method === 'Reward');
return rewards[0]?.event?.data[0] as Balance;
return [rewards[0]?.event?.data[0] as Balance, rewards[0]?.event?.data[1] as Balance];
});
// For old v1, the query results have empty spots (subsequently fixed in v2),
@ -122,11 +129,13 @@ async function loadSome (api: ApiPromise, fromHash: Hash, toHash: Hash): Promise
blockHash: headers[index].hash,
blockNumber: headers[index].number.unwrap(),
isEventsEmpty: events[index].length === 0,
reward: rewards[index] || createType(registry, 'Balance'),
parentHash: headers[index].parentHash,
reward: rewards[index][0] || createType(registry, 'Balance'),
sessionIndex: createType(registry, 'SessionIndex', u8aToU8a(
value.isSome ? value.unwrap() : new Uint8Array([])
)),
slashes: slashes[index]
slashes: slashes[index],
treasury: rewards[index][1] || createType(registry, 'Balance')
}));
}

View File

@ -6,6 +6,7 @@ import { Codec } from '@polkadot/types/types';
import { Options, Param, Params } from './types';
import { useEffect, useRef, useState } from 'react';
import { isUndefined } from '@polkadot/util';
import { dummyPromise, extractParams, transformIdentity } from './util';
@ -20,6 +21,11 @@ interface TrackFn {
(a: Param, b: Param, cb: TrackFnCallback): TrackFnResult;
(a: Param, cb: TrackFnCallback): TrackFnResult;
(cb: TrackFnCallback): TrackFnResult;
meta?: {
type: {
isDoubleMap: boolean;
};
};
}
// tracks a stream, typically an api.* call (derive, rpc, query) that
@ -35,10 +41,12 @@ export default function trackStream <T> (fn: TrackFn | undefined, params: Params
tracker.current.subscriber = dummyPromise;
};
const _subscribe = (params: Params): void => {
const validParams = params.filter((p): boolean => !isUndefined(p));
_unsubscribe();
setImmediate((): void => {
tracker.current.subscriber = fn
tracker.current.subscriber = fn && (!fn.meta || !fn.meta.type?.isDoubleMap || validParams.length === 2)
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore We tried to get the typings right, close but no cigar...
? fn(...params, (value: any): void => setValue(transform(value)))

View File

@ -1,19 +0,0 @@
// Copyright 2017-2019 @polkadot/react-components authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountId, Balance, BlockNumber, Hash, SessionIndex } from '@polkadot/types/interfaces';
export interface Slash {
accountId: AccountId;
amount: Balance;
}
export interface SessionRewards {
blockHash: Hash;
blockNumber: BlockNumber;
isEventsEmpty: boolean;
reward: Balance;
sessionIndex: SessionIndex;
slashes: Slash[];
}