Pass online/offline status indicators with staking accounts (#1265)
* staking online status * restrict onlineStatus.online to validators * get onlineStatus for stash/validator only * tests * fixes * Add offline * Revert stash, change to stashId, linting * Heartbeat signature * Make CodeClimate (slightly) happier * keep compatibility * change status type * rv null possible * cleanup
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
// Copyright 2017-2019 @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.
|
||||
|
||||
export * from './receivedHeartbeats';
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2017-2019 @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 { AccountId, SessionIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
import { of, Observable } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ApiInterfaceRx } from '@polkadot/api/types';
|
||||
import { Bytes } from '@polkadot/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* @description Return a boolean array indicating whether the passed accounts had received heartbeats in the current session
|
||||
*/
|
||||
export function receivedHeartbeats (api: ApiInterfaceRx): (addresses: (AccountId | string)[]) => Observable<boolean[]> {
|
||||
return (addresses: (AccountId | string)[]): Observable<boolean[]> => {
|
||||
return api.query.imOnline && api.query.imOnline.receivedHeartbeats
|
||||
? api.query.session
|
||||
.currentIndex<SessionIndex>()
|
||||
.pipe(
|
||||
switchMap((currentIndex): Observable<Bytes[]> =>
|
||||
api.query.imOnline.receivedHeartbeats.multi(
|
||||
addresses.map((address): [SessionIndex, AccountId | string] =>
|
||||
[currentIndex, address]
|
||||
)
|
||||
)
|
||||
),
|
||||
map((heartbeats): boolean[] =>
|
||||
heartbeats.map((heartbeat): boolean => !heartbeat.isEmpty)
|
||||
),
|
||||
drr()
|
||||
)
|
||||
: of([...new Array(addresses.length).keys()].map((): boolean => false));
|
||||
};
|
||||
}
|
||||
@@ -61,6 +61,10 @@ describe('derive', (): void => {
|
||||
testFunction(api)('session', 'eraLength', []);
|
||||
testFunction(api)('session', 'eraProgress', []);
|
||||
testFunction(api)('session', 'sessionProgress', []);
|
||||
|
||||
testFunction(api)('staking', 'info', []);
|
||||
testFunction(api)('staking', 'recentlyOffline', []);
|
||||
testFunction(api)('staking', 'controllers', []);
|
||||
});
|
||||
|
||||
describe('custom', (): void => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import * as chain from './chain';
|
||||
import * as contracts from './contracts';
|
||||
import * as democracy from './democracy';
|
||||
import * as elections from './elections';
|
||||
import * as imOnline from './imOnline';
|
||||
import * as session from './session';
|
||||
import * as staking from './staking';
|
||||
|
||||
@@ -56,7 +57,7 @@ function injectFunctions<AllSections> (api: ApiInterfaceRx, allSections: AllSect
|
||||
}, {} as unknown as DeriveSections<AllSections>);
|
||||
}
|
||||
|
||||
export const derive = { accounts, balances, chain, contracts, democracy, elections, session, staking };
|
||||
export const derive = { accounts, balances, chain, contracts, democracy, elections, imOnline, session, staking };
|
||||
export type Derive = typeof derive;
|
||||
|
||||
// FIXME I have no idea how to get this done
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
|
||||
export * from './controllers';
|
||||
export * from './info';
|
||||
export * from './recentlyOffline';
|
||||
|
||||
@@ -19,6 +19,7 @@ import { isUndefined } from '@polkadot/util';
|
||||
|
||||
import { bestNumber } from '../chain/bestNumber';
|
||||
import { eraLength } from '../session/eraLength';
|
||||
import { recentlyOffline } from './recentlyOffline';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
type MultiResult = [Option<Keys>, [
|
||||
@@ -128,20 +129,20 @@ function retrieveMulti (api: ApiInterfaceRx, stashId: AccountId, controllerId: A
|
||||
}
|
||||
|
||||
function retrieveInfo (api: ApiInterfaceRx, stashId: AccountId, controllerId: AccountId): Observable<DerivedStaking> {
|
||||
return (
|
||||
combineLatest([
|
||||
eraLength(api)(),
|
||||
bestNumber(api)(),
|
||||
retrieveMulti(api, stashId, controllerId)
|
||||
])
|
||||
).pipe(
|
||||
map(([eraLength, bestNumber, [nextKeys, [queuedKeys, _stakingLedger, [nominators], rewardDestination, stakers, [validatorPrefs]]]]): DerivedStaking => {
|
||||
return combineLatest([
|
||||
eraLength(api)(),
|
||||
bestNumber(api)(),
|
||||
recentlyOffline(api)(),
|
||||
retrieveMulti(api, stashId, controllerId)
|
||||
]).pipe(
|
||||
map(([eraLength, bestNumber, recentlyOffline, [nextKeys, [queuedKeys, _stakingLedger, [nominators], rewardDestination, stakers, [validatorPrefs]]]]): DerivedStaking => {
|
||||
const stakingLedger = _stakingLedger.unwrapOr(undefined);
|
||||
|
||||
return {
|
||||
accountId: stashId,
|
||||
controllerId,
|
||||
nominators,
|
||||
offline: recentlyOffline[stashId.toString()],
|
||||
redeemable: redeemableSum(stakingLedger, eraLength, bestNumber),
|
||||
rewardDestination,
|
||||
stakers,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2017-2019 @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 { AccountId, BlockNumber } from '@polkadot/types/interfaces';
|
||||
import { Codec } from '@polkadot/types/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterfaceRx } from '@polkadot/api/types';
|
||||
import { DerivedRecentlyOffline } from '../types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* @description Retrieve a keyed record of accounts recently reported to be offline
|
||||
*/
|
||||
export function recentlyOffline (api: ApiInterfaceRx): () => Observable<DerivedRecentlyOffline> {
|
||||
return (): Observable<DerivedRecentlyOffline> =>
|
||||
api.query.staking
|
||||
.recentlyOffline<[AccountId, BlockNumber, BN][] & Codec>()
|
||||
.pipe(
|
||||
map((recentlyOffline): DerivedRecentlyOffline =>
|
||||
recentlyOffline.reduce((result: DerivedRecentlyOffline, [accountId, blockNumber, count]): DerivedRecentlyOffline => {
|
||||
const key = accountId.toString();
|
||||
|
||||
if (!result[key]) {
|
||||
result[key] = [];
|
||||
}
|
||||
|
||||
result[key].push({
|
||||
blockNumber,
|
||||
count
|
||||
});
|
||||
|
||||
return result;
|
||||
},
|
||||
{})
|
||||
),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
@@ -51,6 +51,13 @@ export interface DerivedFees {
|
||||
transferFee: BN;
|
||||
}
|
||||
|
||||
export interface RecentlyOffline {
|
||||
blockNumber: BlockNumber;
|
||||
count: BN;
|
||||
}
|
||||
|
||||
export type DerivedRecentlyOffline = Record<string, RecentlyOffline[]>;
|
||||
|
||||
export interface DerivedReferendumVote {
|
||||
accountId: AccountId;
|
||||
balance: Balance;
|
||||
@@ -70,7 +77,22 @@ export interface DerivedSessionInfo {
|
||||
sessionProgress: BN;
|
||||
}
|
||||
|
||||
export interface DerivedStaking {
|
||||
export type DerivedStakingAccount = [AccountId, DerivedStakingOnlineStatus];
|
||||
|
||||
export type DerivedStakingAccounts = DerivedStakingAccount[];
|
||||
|
||||
export interface DerivedStakingOnlineStatus {
|
||||
online?: {
|
||||
isOnline: boolean;
|
||||
blockNumber?: BlockNumber;
|
||||
};
|
||||
offline?: {
|
||||
blockNumber: BlockNumber;
|
||||
count: BN;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface DerivedStaking extends DerivedStakingOnlineStatus {
|
||||
accountId: AccountId;
|
||||
controllerId?: AccountId;
|
||||
nextSessionId?: AccountId;
|
||||
|
||||
Reference in New Issue
Block a user