From e98e8d6a0d7560240fe5e34e24e494fd5d0f26cd Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 19 Jan 2021 13:15:18 +0100 Subject: [PATCH] Type capability detection (#3075) * Type capability detection * re-add Observable export * Remove unused exports * Decorate capabilities * log capabilities * Detect capabilities for historic * Re-add logging * Log * Rework ValidatorPrefs types --- packages/api/src/base/Init.ts | 24 ++++++- packages/api/src/base/util.ts | 69 +++++++++++++++++++ packages/types/src/augment/registry.ts | 11 +-- .../src/interfaces/staking/definitions.ts | 5 +- .../types/src/interfaces/staking/types.ts | 21 +++--- packages/types/src/types/index.ts | 1 + 6 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 packages/api/src/base/util.ts diff --git a/packages/api/src/base/Init.ts b/packages/api/src/base/Init.ts index 1e4f0fea7f..f1f37694d9 100644 --- a/packages/api/src/base/Init.ts +++ b/packages/api/src/base/Init.ts @@ -14,12 +14,13 @@ import { Metadata } from '@polkadot/metadata'; import { TypeRegistry } from '@polkadot/types/create'; import { LATEST_EXTRINSIC_VERSION } from '@polkadot/types/extrinsic/Extrinsic'; import { getSpecAlias, getSpecRpc, getSpecTypes, getUpgradeVersion } from '@polkadot/types-known'; -import { assert, BN_ZERO, logger, u8aEq, u8aToU8a } from '@polkadot/util'; +import { assert, BN_ZERO, logger, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util'; import { cryptoWaitReady } from '@polkadot/util-crypto'; import { of } from '@polkadot/x-rxjs'; import { map, switchMap } from '@polkadot/x-rxjs/operators'; import { Decorate } from './Decorate'; +import { detectedCapabilities } from './util'; const KEEPALIVE_INTERVAL = 15000; const DEFAULT_BLOCKNUMBER = { unwrap: () => BN_ZERO }; @@ -135,6 +136,11 @@ export abstract class Init extends Decorate { // nothing has been found, construct new const metadata = await this._rpcCore.state.getMetadata(header.parentHash).toPromise(); const registry = this._initRegistry(new TypeRegistry(), this._runtimeChain as Text, version, metadata); + + // For now, since this is new we ignore the capability lookups (this could be useful once proven) + // this._detectCapabilities(registry, blockHash); + + // add our new registry const result = { isDefault: false, lastBlockHash, metadata, metadataConsts: null, registry, specVersion: version.specVersion }; this.#registries.push(result); @@ -180,6 +186,19 @@ export abstract class Init extends Decorate { return [source.genesisHash, source.runtimeMetadata]; } + private _detectCapabilities (registry?: Registry, blockHash?: string | Uint8Array): void { + detectedCapabilities(this._rx, blockHash) + .toPromise() + .then((types): void => { + if (Object.keys(types).length) { + (registry || this.registry).register(types as Record); + + l.log(`Capabilities detected${blockHash ? ` (${u8aToHex(u8aToU8a(blockHash))})` : ''}: ${JSON.stringify(types)}`); + } + }) + .catch(l.error); + } + // subscribe to metadata updates, inject the types on changes private _subscribeUpdates (): void { if (this.#updateSub || !this.hasSubscriptions) { @@ -212,6 +231,7 @@ export abstract class Init extends Decorate { // clear the registry types to ensure that we override correctly this._initRegistry(thisRegistry.registry.init(), this._runtimeChain as Text, version, metadata); this.injectMetadata(metadata, false, thisRegistry.registry); + this._detectCapabilities(thisRegistry.registry); return true; }) @@ -279,7 +299,9 @@ export abstract class Init extends Decorate { this._rx.genesisHash = this._genesisHash; this._rx.runtimeVersion = this._runtimeVersion; + // inject metadata and adjust the types as detected this.injectMetadata(metadata, true); + this._detectCapabilities(); // derive is last, since it uses the decorated rx this._rx.derive = this._decorateDeriveRx(this._rxDecorateMethod); diff --git a/packages/api/src/base/util.ts b/packages/api/src/base/util.ts new file mode 100644 index 0000000000..ee17f4b1a5 --- /dev/null +++ b/packages/api/src/base/util.ts @@ -0,0 +1,69 @@ +// Copyright 2017-2021 @polkadot/api-derive authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { ApiInterfaceRx } from '@polkadot/api/types'; +import type { bool } from '@polkadot/types'; +import type { Releases } from '@polkadot/types/interfaces'; + +import { combineLatest, Observable, of } from '@polkadot/x-rxjs'; +import { map, take } from '@polkadot/x-rxjs/operators'; + +// the order and types needs to map with the all array setup below +type Extracted = [bool | null, bool | null, Releases | null]; + +interface DetectedTypes { + AccountInfo: 'AccountInfoWithRefCount' | 'AccountInfoWithProviders', + ValidatorPrefs: 'ValidatorPrefsWithBlocked' | 'ValidatorPrefsWithCommission'; +} + +function mapCapabilities ([systemRefcount32, systemRefcountDual, stakingVersion]: Extracted): Partial { + const types: Partial = {}; + + // AccountInfo + if (systemRefcountDual && systemRefcountDual.isTrue) { + types.AccountInfo = 'AccountInfoWithProviders'; + } else if (systemRefcount32 && systemRefcount32.isTrue) { + types.AccountInfo = 'AccountInfoWithRefCount'; + } + + // ValidatorPrefs + if (stakingVersion) { + if (stakingVersion.index >= 4) { // v1 = index 0, V5 = index 4 + types.ValidatorPrefs = 'ValidatorPrefsWithBlocked'; + } else { + types.ValidatorPrefs = 'ValidatorPrefsWithCommission'; + } + } + + return types; +} + +/** + * @description Query the chain for the specific capabilities + */ +export function detectedCapabilities (api: ApiInterfaceRx, blockHash?: Uint8Array | string | undefined): Observable> { + const all = [ + api.query.system?.upgradedToU32RefCount, + api.query.system?.upgradedToDualRefCount, + api.query.staking?.storageVersion + ]; + const included = all.map((c) => !!c); + const filtered = all.filter((_, index) => included[index]); + + return ( + filtered.length + ? blockHash + ? combineLatest(filtered.map((c) => c.at(blockHash))) + : api.queryMulti(filtered) + : of([]) + ).pipe( + map((results): Partial => { + let offset = -1; + + return mapCapabilities( + included.map((isIncluded) => isIncluded ? results[++offset] : null) as Extracted + ); + }), + take(1) + ); +} diff --git a/packages/types/src/augment/registry.ts b/packages/types/src/augment/registry.ts index 76616b22a0..3159904664 100644 --- a/packages/types/src/augment/registry.ts +++ b/packages/types/src/augment/registry.ts @@ -43,7 +43,7 @@ import type { SiField, SiLookupTypeId, SiPath, SiType, SiTypeDef, SiTypeDefArray import type { Period, Priority, SchedulePeriod, SchedulePriority, Scheduled, ScheduledTo254, TaskAddress } from '@polkadot/types/interfaces/scheduler'; import type { FullIdentification, IdentificationTuple, Keys, MembershipProof, SessionIndex, SessionKeys1, SessionKeys2, SessionKeys3, SessionKeys4, SessionKeys5, SessionKeys6, SessionKeys7, SessionKeys8, SessionKeys9, ValidatorCount } from '@polkadot/types/interfaces/session'; import type { Bid, BidKind, SocietyJudgement, SocietyVote, StrikeCount, VouchingStatus } from '@polkadot/types/interfaces/society'; -import type { ActiveEraInfo, CompactAssignments, CompactAssignmentsTo257, CompactScore, CompactScoreCompact, ElectionCompute, ElectionResult, ElectionScore, ElectionSize, ElectionStatus, EraIndex, EraPoints, EraRewardPoints, EraRewards, Exposure, Forcing, IndividualExposure, KeyType, MomentOf, Nominations, NominatorIndex, NominatorIndexCompact, OffchainAccuracy, OffchainAccuracyCompact, PhragmenScore, Points, RewardDestination, RewardDestinationTo257, RewardPoint, SlashJournalEntry, SlashingSpans, SlashingSpansTo204, SpanIndex, SpanRecord, StakingLedger, StakingLedgerTo223, StakingLedgerTo240, UnappliedSlash, UnappliedSlashOther, UnlockChunk, ValidatorIndex, ValidatorIndexCompact, ValidatorPrefs, ValidatorPrefsBlocked, ValidatorPrefsTo145, ValidatorPrefsTo196 } from '@polkadot/types/interfaces/staking'; +import type { ActiveEraInfo, CompactAssignments, CompactAssignmentsTo257, CompactScore, CompactScoreCompact, ElectionCompute, ElectionResult, ElectionScore, ElectionSize, ElectionStatus, EraIndex, EraPoints, EraRewardPoints, EraRewards, Exposure, Forcing, IndividualExposure, KeyType, MomentOf, Nominations, NominatorIndex, NominatorIndexCompact, OffchainAccuracy, OffchainAccuracyCompact, PhragmenScore, Points, RewardDestination, RewardDestinationTo257, RewardPoint, SlashJournalEntry, SlashingSpans, SlashingSpansTo204, SpanIndex, SpanRecord, StakingLedger, StakingLedgerTo223, StakingLedgerTo240, UnappliedSlash, UnappliedSlashOther, UnlockChunk, ValidatorIndex, ValidatorIndexCompact, ValidatorPrefs, ValidatorPrefsTo145, ValidatorPrefsTo196, ValidatorPrefsWithBlocked, ValidatorPrefsWithCommission } from '@polkadot/types/interfaces/staking'; import type { ApiId, KeyValueOption, ReadProof, RuntimeVersion, RuntimeVersionApi, StorageChangeSet } from '@polkadot/types/interfaces/state'; import type { WeightToFeeCoefficient } from '@polkadot/types/interfaces/support'; import type { AccountInfo, AccountInfoWithProviders, AccountInfoWithRefCount, ApplyExtrinsicResult, BlockWeights, ChainProperties, ChainType, ConsumedWeight, DigestOf, DispatchClass, DispatchError, DispatchErrorModule, DispatchErrorTo198, DispatchInfo, DispatchInfoTo190, DispatchInfoTo244, DispatchOutcome, DispatchResult, DispatchResultOf, DispatchResultTo198, Event, EventId, EventIndex, EventRecord, Health, InvalidTransaction, Key, LastRuntimeUpgradeInfo, NetworkState, NetworkStatePeerset, NetworkStatePeersetInfo, NodeRole, NotConnectedPeer, Peer, PeerEndpoint, PeerEndpointAddr, PeerInfo, PeerPing, PerDispatchClass, Phase, RawOrigin, RefCount, RefCountTo259, SyncState, SystemOrigin, TransactionValidityError, UnknownTransaction, WeightPerClass } from '@polkadot/types/interfaces/system'; @@ -804,9 +804,10 @@ declare module '@polkadot/types/types/registry' { 'Option': Option; 'Option': Option; 'Option': Option; - 'Option': Option; 'Option': Option; 'Option': Option; + 'Option': Option; + 'Option': Option; 'Option': Option; 'Option': Option; 'Option': Option; @@ -1511,9 +1512,10 @@ declare module '@polkadot/types/types/registry' { 'Vec': Vec; 'Vec': Vec; 'Vec': Vec; - 'Vec': Vec; 'Vec': Vec; 'Vec': Vec; + 'Vec': Vec; + 'Vec': Vec; 'Vec': Vec; 'Vec': Vec; 'Vec': Vec; @@ -2218,9 +2220,10 @@ declare module '@polkadot/types/types/registry' { ValidatorIndex: ValidatorIndex; ValidatorIndexCompact: ValidatorIndexCompact; ValidatorPrefs: ValidatorPrefs; - ValidatorPrefsBlocked: ValidatorPrefsBlocked; ValidatorPrefsTo145: ValidatorPrefsTo145; ValidatorPrefsTo196: ValidatorPrefsTo196; + ValidatorPrefsWithBlocked: ValidatorPrefsWithBlocked; + ValidatorPrefsWithCommission: ValidatorPrefsWithCommission; ValidatorSignature: ValidatorSignature; ValidityAttestation: ValidityAttestation; VersionedMultiAsset: VersionedMultiAsset; diff --git a/packages/types/src/interfaces/staking/definitions.ts b/packages/types/src/interfaces/staking/definitions.ts index ea82125a37..e50a9439e0 100644 --- a/packages/types/src/interfaces/staking/definitions.ts +++ b/packages/types/src/interfaces/staking/definitions.ts @@ -190,10 +190,11 @@ export default { }, ValidatorIndex: 'u16', ValidatorIndexCompact: 'Compact', - ValidatorPrefs: { + ValidatorPrefs: 'ValidatorPrefsWithCommission', + ValidatorPrefsWithCommission: { commission: 'Compact' }, - ValidatorPrefsBlocked: { + ValidatorPrefsWithBlocked: { commission: 'Compact', blocked: 'bool' }, diff --git a/packages/types/src/interfaces/staking/types.ts b/packages/types/src/interfaces/staking/types.ts index 07b93d0439..9617406e80 100644 --- a/packages/types/src/interfaces/staking/types.ts +++ b/packages/types/src/interfaces/staking/types.ts @@ -262,15 +262,7 @@ export interface ValidatorIndex extends u16 {} export interface ValidatorIndexCompact extends Compact {} /** @name ValidatorPrefs */ -export interface ValidatorPrefs extends Struct { - readonly commission: Compact; -} - -/** @name ValidatorPrefsBlocked */ -export interface ValidatorPrefsBlocked extends Struct { - readonly commission: Compact; - readonly blocked: bool; -} +export interface ValidatorPrefs extends ValidatorPrefsWithCommission {} /** @name ValidatorPrefsTo145 */ export interface ValidatorPrefsTo145 extends Struct { @@ -283,4 +275,15 @@ export interface ValidatorPrefsTo196 extends Struct { readonly validatorPayment: Compact; } +/** @name ValidatorPrefsWithBlocked */ +export interface ValidatorPrefsWithBlocked extends Struct { + readonly commission: Compact; + readonly blocked: bool; +} + +/** @name ValidatorPrefsWithCommission */ +export interface ValidatorPrefsWithCommission extends Struct { + readonly commission: Compact; +} + export type PHANTOM_STAKING = 'staking'; diff --git a/packages/types/src/types/index.ts b/packages/types/src/types/index.ts index 54fe24a4a1..6442d58a04 100644 --- a/packages/types/src/types/index.ts +++ b/packages/types/src/types/index.ts @@ -13,4 +13,5 @@ export * from './extrinsic'; export * from './interfaces'; export * from './registry'; +// used inside augmented definitions export type { Observable } from '@polkadot/x-rxjs';