Remove capabilities (#4303)

This commit is contained in:
Jaco
2021-12-08 20:18:38 +02:00
committed by GitHub
parent fd6ba08f58
commit dcb7760a0e
6 changed files with 33 additions and 260 deletions
+19 -41
View File
@@ -122,54 +122,32 @@ function getModuleInstances (api: ApiInterfaceRx, specName: string, moduleName:
*/
/** @internal */
function injectFunctions (instanceId: string, api: ApiInterfaceRx, derives: DeriveCustom): ExactDerive {
const queryKeys = Object.keys(api.query);
const specName = api.runtimeVersion.specName.toString();
const filterQueryKeys = (q: string) =>
queryKeys.includes(q);
const filterInstances = (q: string) =>
getModuleInstances(api, specName, q).some(filterQueryKeys);
const filterMethods = (instances: string[]) =>
(m: string) =>
instances.some((q) =>
queryKeys.includes(q) &&
!!api.query[q][m]
);
const isIncluded = (s: string) => (
!checks[s] ||
(
(
checks[s].instances.some(filterQueryKeys) &&
(
!checks[s].methods.length ||
checks[s].methods.every(filterMethods(checks[s].instances))
)
) ||
(
checks[s].withDetect &&
checks[s].instances.some(filterInstances)
)
)
);
const getKeys = (section: string) =>
Object.keys(derives[section]);
const creator = (section: string, method: string) =>
derives[section][method](instanceId, api);
const result: Record<string, Record<string, AnyFunction>> = {};
const names = Object.keys(derives);
const keys = Object.keys(api.query);
const specName = api.runtimeVersion.specName.toString();
const filterKeys = (q: string) => keys.includes(q);
const filterInstances = (q: string) => getModuleInstances(api, specName, q).some(filterKeys);
const filterMethods = (all: string[]) => (m: string) => all.some((q) => keys.includes(q) && api.query[q][m]);
const getKeys = (s: string) => Object.keys(derives[s]);
const creator = (s: string, m: string) => derives[s][m](instanceId, api);
const isIncluded = (c: string) => (!checks[c] || (
(checks[c].instances.some(filterKeys) && (
!checks[c].methods.length ||
checks[c].methods.every(filterMethods(checks[c].instances))
)) ||
(
checks[c].withDetect &&
checks[c].instances.some(filterInstances)
)
));
for (let i = 0; i < names.length; i++) {
const name = names[i];
if (isIncluded(name)) {
isIncluded(name) &&
lazyDeriveSection(result, name, getKeys, creator);
}
}
return result as ExactDerive;
+6 -4
View File
@@ -6,6 +6,10 @@ import type { AccountId, Balance, BlockNumber, Hash, PropIndex, Proposal, Refere
import type { PalletDemocracyReferendumStatus, PalletDemocracyVoteThreshold } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
export interface AtBlock {
at: BlockNumber;
}
export interface DeriveDemocracyLock {
balance: Balance;
isDelegated: boolean;
@@ -16,15 +20,13 @@ export interface DeriveDemocracyLock {
vote: Vote;
}
export interface DeriveProposalImage {
at: BlockNumber;
export interface DeriveProposalImage extends AtBlock {
balance: Balance;
proposal?: Proposal;
proposer: AccountId;
}
export interface DeriveDispatch {
at: BlockNumber;
export interface DeriveDispatch extends AtBlock {
index: ReferendumIndex;
imageHash: Hash;
image?: DeriveProposalImage;
+2 -20
View File
@@ -15,10 +15,9 @@ import { firstValueFrom, map, of, switchMap } from 'rxjs';
import { Metadata, TypeRegistry } from '@polkadot/types';
import { getSpecAlias, getSpecExtensions, getSpecHasher, getSpecRpc, getSpecTypes, getUpgradeVersion } from '@polkadot/types-known';
import { assert, BN_ZERO, isUndefined, logger, objectSpread, stringify, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util';
import { assert, BN_ZERO, isUndefined, logger, objectSpread, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { detectedCapabilities } from './capabilities';
import { Decorate } from './Decorate';
const KEEPALIVE_INTERVAL = 10000;
@@ -264,20 +263,6 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
return [source.genesisHash, source.runtimeMetadata];
}
private _detectCapabilities (registry: Registry, blockHash?: string | Uint8Array): boolean {
firstValueFrom(detectedCapabilities(this._rx, blockHash))
.then((types): void => {
if (Object.keys(types).length) {
registry.register(types as Record<string, string>);
l.debug(() => `Capabilities detected${blockHash ? ` (${u8aToHex(u8aToU8a(blockHash))})` : ''}: ${stringify(types)}`);
}
})
.catch(undefined);
return true;
}
// subscribe to metadata updates, inject the types on changes
private _subscribeUpdates (): void {
if (this.#updateSub || !this.hasSubscriptions) {
@@ -308,7 +293,7 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
this._initRegistry(thisRegistry.registry.init(), this._runtimeChain as Text, version, metadata);
this._injectMetadata(thisRegistry, false);
return this._detectCapabilities(thisRegistry.registry);
return true;
})
)
)
@@ -369,9 +354,6 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
this._rx.derive = this._decorateDeriveRx(this._rxDecorateMethod);
this._derive = this._decorateDerive(this._decorateMethod);
// detect the on-chain capabilities
this._detectCapabilities(this.registry);
return true;
}
-191
View File
@@ -1,191 +0,0 @@
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Observable } from 'rxjs';
import type { ApiInterfaceRx } from '@polkadot/api/types';
import type { Raw, u32 } from '@polkadot/types';
import type { Releases } from '@polkadot/types/interfaces';
import type { InterfaceTypes } from '@polkadot/types/types';
import { catchError, combineLatest, map, of, take } from 'rxjs';
import { assert, compactFromU8a } from '@polkadot/util';
// the order and types needs to map with the all array setup below
type ExtractedQ = [Releases | null];
type ExtractedR = [Raw | null, Raw | null];
type ExtractedC = [u32 | null, u32 | null];
type DetectedKeys = keyof Pick<InterfaceTypes, 'AccountInfo' | 'ValidatorPrefs'>;
type DetectedValues = keyof InterfaceTypes;
interface DetectedTypes extends Record<DetectedKeys, DetectedValues> {
AccountInfo: 'AccountInfoWithRefCount' | 'AccountInfoWithDualRefCount' | 'AccountInfoWithTripleRefCount',
Keys: 'SessionKeys1' | 'SessionKeys2' | 'SessionKeys3' | 'SessionKeys4' | 'SessionKeys5' | 'SessionKeys6' | 'SessionKeys7' | 'SessionKeys8' | 'SessionKeys9' | 'SessionKeys10';
RefCount: 'u8' | 'u32',
SlotRange: Record<string, unknown>;
ValidatorPrefs: 'ValidatorPrefsWithBlocked' | 'ValidatorPrefsWithCommission';
WinningData: string;
}
interface AvailableMap<T> {
filtered: T[];
included: boolean[];
original: T[];
}
interface Constants {
accountIdLength: number;
refcount1Length: number;
refcount2Length: number;
refcount3Length: number;
}
const NumberMap = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten'];
function mapCapabilities ({ accountIdLength, refcount1Length, refcount2Length, refcount3Length }: Constants, [leasePeriodsPerSlot, slotRangeCount]: ExtractedC, [stakingVersion]: ExtractedQ, [keys, accountInfo]: ExtractedR): Partial<DetectedTypes> {
const types: Partial<DetectedTypes> = {};
// AccountInfo
if (accountInfo) {
const length = accountInfo.length;
if (length === refcount1Length) {
types.AccountInfo = 'AccountInfoWithRefCount';
} else if (length === refcount2Length) {
types.AccountInfo = 'AccountInfoWithDualRefCount';
} else if (length === refcount3Length) {
types.AccountInfo = 'AccountInfoWithTripleRefCount';
}
}
// ValidatorPrefs
if (stakingVersion) {
if (stakingVersion.index >= 4) { // v1 = index 0, V5 = index 4
types.ValidatorPrefs = 'ValidatorPrefsWithBlocked';
} else {
types.ValidatorPrefs = 'ValidatorPrefsWithCommission';
}
}
// Keys
if (keys) {
try {
const [offset, numItems] = compactFromU8a(keys);
const tupleLength = (keys.length - offset) / numItems.toNumber();
const numIds = tupleLength / accountIdLength;
const numIdsRound = Math.floor(numIds);
assert(numIds >= 2 && numIds <= 11, () => `Detected ${numIds} in Keys, should be >= 2 and <= 11`);
if (numIdsRound !== numIds) {
// Beefy?
if ((((numIdsRound - 1) * accountIdLength) + 33) === tupleLength) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
types.Keys = `SessionKeys${numIdsRound - 1}B`;
} else {
assert(false, () => `Expected integer number of keys, found ${numIds.toFixed(2)}`);
}
} else {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
types.Keys = `SessionKeys${numIds - 1}`;
}
} catch {
// ignore
}
}
// auctions
if (leasePeriodsPerSlot && slotRangeCount) {
const _enum: string[] = [];
for (let i = 0; leasePeriodsPerSlot.gtn(i); i++) {
for (let j = i; leasePeriodsPerSlot.gtn(j); j++) {
_enum.push(`${NumberMap[i]}${NumberMap[j]}`);
}
}
types.SlotRange = { _enum };
types.WinningData = `[WinningDataEntry; ${slotRangeCount.toNumber()}]`;
}
return types;
}
function filterEntries <T> (original: T[]): AvailableMap<T> {
const included = original.map((c) => !!c);
return {
filtered: original.filter((_, index) => included[index]),
included,
original
};
}
function extractResults <R, T = unknown> (results: unknown[], map: AvailableMap<T>): R {
let offset = -1;
return map.included.map((isIncluded) =>
isIncluded
? results[++offset]
: null
) as unknown as R;
}
/**
* @description Query the chain for the specific capabilities
*/
export function detectedCapabilities (api: ApiInterfaceRx, blockHash?: Uint8Array | string | undefined): Observable<Partial<DetectedTypes>> {
const emptyAccountId = api.registry.createType('AccountId');
const consts = filterEntries([
api.consts.auctions?.leasePeriodsPerSlot,
api.consts.auctions?.slotRangeCount
]);
const queries = filterEntries([
api.query.staking?.storageVersion
]);
const raws = filterEntries([
api.query.session?.queuedKeys.key(),
api.query.system?.account?.key(emptyAccountId)
]);
return combineLatest([
consts.filtered.length
? blockHash
// FIXME consts don't have .at as of yet...
? of([])
: of(consts.filtered)
: of([]),
queries.filtered.length
? blockHash
? combineLatest(queries.filtered.map((c) => c.at(blockHash)))
: api.queryMulti(queries.filtered)
: of([]),
raws.filtered.length
? blockHash
? combineLatest(raws.filtered.map((k) => api.rpc.state.getStorage.raw(k, blockHash)))
: combineLatest(raws.filtered.map((k) => api.rpc.state.getStorage.raw(k)))
: of([])
]).pipe(
map(([cResults, qResults, rResults]): Partial<DetectedTypes> =>
mapCapabilities(
{
accountIdLength: emptyAccountId.encodedLength,
refcount1Length: api.registry.createType('AccountInfoWithRefCount').encodedLength,
refcount2Length: api.registry.createType('AccountInfoWithDualRefCount').encodedLength,
refcount3Length: api.registry.createType('AccountInfoWithTripleRefCount').encodedLength
},
extractResults<ExtractedC>(cResults, consts),
extractResults<ExtractedQ>(qResults, queries),
extractResults<ExtractedR>(rResults.map((r) => api.registry.createType('Raw', r)), raws)
)
),
take(1),
catchError(() => of({}))
);
}
+4 -4
View File
@@ -6,20 +6,20 @@ import type { AnyFunction, Callback, DefinitionRpc } from '@polkadot/types/types
import { ApiTypes, PromiseResult, Push, RxResult, UnsubscribePromise } from './base';
export interface RpcRxResult<F extends AnyFunction> extends RxResult<F> {
export interface RxRpcResult<F extends AnyFunction> extends RxResult<F> {
raw <T> (...args: Parameters<F>): Observable<T>;
meta: DefinitionRpc;
}
export interface RpcPromiseResult<F extends AnyFunction> extends PromiseResult<F> {
export interface PromiseRpcResult<F extends AnyFunction> extends PromiseResult<F> {
raw <T> (...args: Parameters<F>): Promise<T>;
raw <T> (...args: Push<Parameters<F>, Callback<T>>): UnsubscribePromise;
meta: DefinitionRpc;
}
export type RpcMethodResult<ApiType extends ApiTypes, F extends AnyFunction> = ApiType extends 'rxjs'
? RpcRxResult<F>
: RpcPromiseResult<F>;
? RxRpcResult<F>
: PromiseRpcResult<F>;
export type DecoratedRpcSection<ApiType extends ApiTypes, Section> = {
[Method in keyof Section]: Section[Method] extends AnyFunction