From 049a1a59f56974cdbe034dcee18a0bfbe23dd67b Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 10 Mar 2020 08:20:54 +0100 Subject: [PATCH] Rework eras* calls with minimum information (#2018) --- .../api-derive/src/staking/erasExposure.ts | 59 +++++++++++-------- .../api-derive/src/staking/erasHistoric.ts | 46 +++++++++++++++ packages/api-derive/src/staking/erasPoints.ts | 45 +++----------- .../{erasRewards.ts => erasRewards.ts-unused} | 5 ++ packages/api-derive/src/staking/index.ts | 5 +- .../{stakerRewards.ts => stakerExposure.ts} | 17 +++--- packages/api-derive/src/staking/types.ts | 37 +++++------- 7 files changed, 122 insertions(+), 92 deletions(-) create mode 100644 packages/api-derive/src/staking/erasHistoric.ts rename packages/api-derive/src/staking/{erasRewards.ts => erasRewards.ts-unused} (90%) rename packages/api-derive/src/staking/{stakerRewards.ts => stakerExposure.ts} (58%) diff --git a/packages/api-derive/src/staking/erasExposure.ts b/packages/api-derive/src/staking/erasExposure.ts index 6f6924b346..31144180ee 100644 --- a/packages/api-derive/src/staking/erasExposure.ts +++ b/packages/api-derive/src/staking/erasExposure.ts @@ -3,49 +3,60 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; -import { Exposure } from '@polkadot/types/interfaces'; -import { DeriveEraExposure, DeriveEraExposures, DeriveEraPointsAll, DeriveEraValPoints } from '../types'; +import { EraIndex, Exposure } from '@polkadot/types/interfaces'; +import { DeriveEraExposure, DeriveEraNominatorExposure, DeriveEraValidatorExposure } from '../types'; +import BN from 'bn.js'; import { Observable, combineLatest, of } from 'rxjs'; import { map, switchMap, take } from 'rxjs/operators'; import { StorageKey } from '@polkadot/types'; import { memo } from '../util'; -function mapValidators (stakers: [StorageKey, Exposure][], validators: DeriveEraValPoints): DeriveEraExposures { - return stakers.reduce((result: DeriveEraExposures, [key, exposure]): DeriveEraExposures => { - const validatorId = key.args[1].toString(); - - result[validatorId] = { - exposure, - points: validators[validatorId] - }; - - return result; - }, {}); +interface Mapped { + nominators: DeriveEraNominatorExposure; + validators: DeriveEraValidatorExposure; } -export function erasExposure (api: ApiInterfaceRx): (withActive?: boolean) => Observable { - return memo((withActive?: boolean): Observable => - api.derive.staking.erasPoints(withActive).pipe( - switchMap((allPoints): Observable<[DeriveEraPointsAll[], [StorageKey, Exposure][][]]> => +function mapStakers (stakers: [StorageKey, Exposure][]): Mapped { + const nominators: DeriveEraNominatorExposure = {}; + const validators: DeriveEraValidatorExposure = {}; + + stakers.forEach(([key, exposure]): void => { + const validatorId = key.args[1].toString(); + + validators[validatorId] = exposure; + + exposure.others.forEach(({ who }, index): void => { + const nominatorId = who.toString(); + + nominators[nominatorId] = nominators[nominatorId] || []; + nominators[nominatorId].push([validatorId, index]); + }); + }); + + return { nominators, validators }; +} + +export function erasExposure (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable { + return memo((withActive?: boolean | BN): Observable => + api.derive.staking.erasHistoric(withActive).pipe( + switchMap((allEras): Observable<[EraIndex[], [StorageKey, Exposure][][]]> => combineLatest([ - of(allPoints), + of(allEras), combineLatest( // we could just do entries over the full set, however the set can be quite large - split it into // batches - may need to re-visit this, or alternatively use pages keys for exceptionally large sets - allPoints.map(({ era }): Observable<[StorageKey, Exposure][]> => + allEras.map((era): Observable<[StorageKey, Exposure][]> => api.query.staking.erasStakers.entries(era).pipe(take(1)) ) ) ]) ), - map(([allPoints, erasStakers]): DeriveEraExposure[] => - allPoints.map(({ era, eraPoints, eraReward, validators }, index): DeriveEraExposure => ({ + map(([allEras, erasStakers]): DeriveEraExposure[] => + allEras.map((era, index): DeriveEraExposure => ({ era, - eraPoints, - eraReward, - validators: mapValidators(erasStakers[index], validators) + ...mapStakers(erasStakers[index]) })) ) ) diff --git a/packages/api-derive/src/staking/erasHistoric.ts b/packages/api-derive/src/staking/erasHistoric.ts new file mode 100644 index 0000000000..dce285fbb8 --- /dev/null +++ b/packages/api-derive/src/staking/erasHistoric.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2020 @polkadot/api-derive 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 { ApiInterfaceRx } from '@polkadot/api/types'; +import { ActiveEraInfo, EraIndex } from '@polkadot/types/interfaces'; + +import BN from 'bn.js'; +import { Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Option, u32 } from '@polkadot/types'; +import { isBoolean, isUndefined } from '@polkadot/util'; + +import { memo } from '../util'; + +export function erasHistoric (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable { + return memo((withActive?: boolean | BN): Observable => + api.query.staking?.activeEra + ? api.queryMulti<[Option, u32]>([ + api.query.staking.activeEra, + api.query.staking.historyDepth + ]).pipe( + map(([activeEraOpt, historyDepth]): EraIndex[] => { + const result: EraIndex[] = []; + const max = historyDepth.toNumber(); + let lastEra = activeEraOpt.unwrapOrDefault().index.subn(withActive === true ? 0 : 1); + + while (lastEra.gten(0) && result.length < max) { + result.push(api.registry.createType('EraIndex', lastEra)); + + lastEra = lastEra.subn(1); + } + + // go from oldest to newest + return result + .filter((era): boolean => + isUndefined(withActive) || isBoolean(withActive) + ? true + : era.gte(withActive) + ) + .reverse(); + }) + ) + : of([]) + ); +} diff --git a/packages/api-derive/src/staking/erasPoints.ts b/packages/api-derive/src/staking/erasPoints.ts index 4551d7e058..37e5d9254f 100644 --- a/packages/api-derive/src/staking/erasPoints.ts +++ b/packages/api-derive/src/staking/erasPoints.ts @@ -3,39 +3,14 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; -import { ActiveEraInfo, Balance, EraIndex, EraRewardPoints } from '@polkadot/types/interfaces'; -import { DeriveEraPointsAll, DeriveEraValPoints } from '../types'; +import { EraIndex, EraRewardPoints } from '@polkadot/types/interfaces'; +import { DeriveEraPoints, DeriveEraValPoints } from '../types'; import { Observable, combineLatest, of } from 'rxjs'; import { map, switchMap, take } from 'rxjs/operators'; -import { Option, u32 } from '@polkadot/types'; import { memo } from '../util'; -function getAvailableIndexes (api: ApiInterfaceRx, withActive?: boolean): Observable { - return api.query.staking?.activeEra - ? api.queryMulti<[Option, u32]>([ - api.query.staking.activeEra, - api.query.staking.historyDepth - ]).pipe( - map(([activeEraOpt, historyDepth]): EraIndex[] => { - const result: EraIndex[] = []; - const max = historyDepth.toNumber(); - let lastEra = activeEraOpt.unwrapOrDefault().index.subn(withActive ? 0 : 1); - - while (lastEra.gten(0) && result.length < max) { - result.push(api.registry.createType('EraIndex', lastEra)); - - lastEra = lastEra.subn(1); - } - - // go from oldest to newest - return result.reverse(); - }) - ) - : of([]); -} - function mapValidators ({ individual }: EraRewardPoints): DeriveEraValPoints { return [...individual.entries()] .filter(([, points]): boolean => points.gtn(0)) @@ -46,25 +21,21 @@ function mapValidators ({ individual }: EraRewardPoints): DeriveEraValPoints { }, {}); } -export function erasPoints (api: ApiInterfaceRx): (withActive?: boolean) => Observable { - return memo((withActive?: boolean): Observable => - getAvailableIndexes(api, withActive).pipe( - switchMap((indexes): Observable<[EraIndex[], EraRewardPoints[], Option[]]> => +export function erasPoints (api: ApiInterfaceRx): (withActive?: boolean) => Observable { + return memo((withActive?: boolean): Observable => + api.derive.staking.erasHistoric(withActive).pipe( + switchMap((indexes): Observable<[EraIndex[], EraRewardPoints[]]> => combineLatest([ of(indexes), indexes.length ? api.query.staking.erasRewardPoints.multi(indexes).pipe(take(1)) - : of([]), - indexes.length - ? api.query.staking.erasValidatorReward.multi>(indexes).pipe(take(1)) : of([]) ]) ), - map(([eras, points, rewards]): DeriveEraPointsAll[] => - eras.map((era, index): DeriveEraPointsAll => ({ + map(([eras, points]): DeriveEraPoints[] => + eras.map((era, index): DeriveEraPoints => ({ era, eraPoints: points[index].total, - eraReward: rewards[index].unwrapOrDefault(), validators: mapValidators(points[index]) })) ) diff --git a/packages/api-derive/src/staking/erasRewards.ts b/packages/api-derive/src/staking/erasRewards.ts-unused similarity index 90% rename from packages/api-derive/src/staking/erasRewards.ts rename to packages/api-derive/src/staking/erasRewards.ts-unused index 15e38af352..5dc79f68a9 100644 --- a/packages/api-derive/src/staking/erasRewards.ts +++ b/packages/api-derive/src/staking/erasRewards.ts-unused @@ -20,6 +20,11 @@ function includeExposure (validatorId: string, { others }: Exposure, nominators: }); } +// , +// indexes.length +// ? api.query.staking.erasValidatorReward.multi>(indexes).pipe(take(1)) +// : of([]) + export function erasRewards (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasExposure(withActive).pipe( diff --git a/packages/api-derive/src/staking/index.ts b/packages/api-derive/src/staking/index.ts index a5bc670a4f..d57c490448 100644 --- a/packages/api-derive/src/staking/index.ts +++ b/packages/api-derive/src/staking/index.ts @@ -4,11 +4,12 @@ export * from './account'; export * from './erasExposure'; +export * from './erasHistoric'; export * from './erasPoints'; -export * from './erasRewards'; +// export * from './erasRewards'; export * from './controllers'; export * from './electedInfo'; export * from './overview'; export * from './query'; -export * from './stakerRewards'; +export * from './stakerExposure'; export * from './validators'; diff --git a/packages/api-derive/src/staking/stakerRewards.ts b/packages/api-derive/src/staking/stakerExposure.ts similarity index 58% rename from packages/api-derive/src/staking/stakerRewards.ts rename to packages/api-derive/src/staking/stakerExposure.ts index c7f0a776cb..c70e63af56 100644 --- a/packages/api-derive/src/staking/stakerRewards.ts +++ b/packages/api-derive/src/staking/stakerExposure.ts @@ -3,22 +3,23 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; -import { DeriveEraExposures, DeriveStakerReward } from '../types'; +import { DeriveStakerExpoure, DeriveEraValidatorExposure } from '../types'; +import BN from 'bn.js'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { memo } from '../util'; -export function stakerRewards (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { - return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => { +export function stakerExposure (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean | BN) => Observable { + return memo((accountId: Uint8Array | string, withActive?: boolean | BN): Observable => { const stakerId = api.registry.createType('AccountId', accountId).toString(); - return api.derive.staking.erasRewards(withActive).pipe( - map((rewards): DeriveStakerReward[] => - rewards.map(({ era, eraPoints, eraReward, nominators: allNominators, validators: allValidators }): DeriveStakerReward => { + return api.derive.staking.erasExposure(withActive).pipe( + map((exposures): DeriveStakerExpoure[] => + exposures.map(({ era, nominators: allNominators, validators: allValidators }): DeriveStakerExpoure => { const isValidator = !!allValidators[stakerId]; - const validators: DeriveEraExposures = {}; + const validators: DeriveEraValidatorExposure = {}; let nominating: [string, number][] = []; if (isValidator) { @@ -31,7 +32,7 @@ export function stakerRewards (api: ApiInterfaceRx): (accountId: Uint8Array | st }); } - return { era, eraPoints, eraReward, isEmpty: !Object.keys(validators).length, isValidator, nominating, validators }; + return { era, isEmpty: !Object.keys(validators).length, isValidator, nominating, validators }; }) ) ); diff --git a/packages/api-derive/src/staking/types.ts b/packages/api-derive/src/staking/types.ts index b35e6cdac5..d14426eefc 100644 --- a/packages/api-derive/src/staking/types.ts +++ b/packages/api-derive/src/staking/types.ts @@ -5,41 +5,36 @@ import { AccountId, Balance, BlockNumber, EraIndex, EraRewardPoints, Exposure, Keys, RewardDestination, RewardPoint, StakingLedger, ValidatorPrefs } from '@polkadot/types/interfaces'; import { DeriveSessionIndexes } from '../session/types'; -interface EraPointInfo { - era: EraIndex; - eraPoints: RewardPoint; - eraReward: Balance; -} - export type DeriveEraValPoints = Record; -export interface DeriveEraPointsAll extends EraPointInfo { +export interface DeriveEraPoints { + era: EraIndex; + eraPoints: RewardPoint; validators: DeriveEraValPoints; } -export interface DeriveEraPoints extends EraPointInfo { +export interface DeriveStakersPoints { + era: EraIndex; + eraPoints: RewardPoint; own: RewardPoint; } -export type DeriveEraExposures = Record; +export type DeriveEraNominatorExposure = Record; -export interface DeriveEraExposure extends EraPointInfo { - validators: DeriveEraExposures; +export type DeriveEraValidatorExposure = Record; + +export interface DeriveEraExposure { + era: EraIndex; + nominators: DeriveEraNominatorExposure; + validators: DeriveEraValidatorExposure; } -export interface DeriveEraRewardsAll extends EraPointInfo { - nominators: Record; - validators: DeriveEraExposures; -} - -export interface DeriveStakerReward extends EraPointInfo { +export interface DeriveStakerExpoure { + era: EraIndex; isEmpty: boolean; isValidator: boolean; nominating: [string, number][]; - validators: DeriveEraExposures; + validators: DeriveEraValidatorExposure; } export interface DerivedStakingElected {