Ensure rpcCore correctly decorates .raw (#4091)
* Ensure rpcCore correctly decorates .json/.raw * .raw encoding changes + memoization
This commit is contained in:
@@ -8,9 +8,13 @@ Contributed:
|
||||
|
||||
Changes:
|
||||
|
||||
- Ecah module now exposes the Substrate-added `palletVersion` storage item
|
||||
- Adjust Websocket `maxReceivedFrameSize` under Node
|
||||
- `TypeDef` structures now contain the `typeName` from metadata v14 on fields
|
||||
- Fix browser bundle imports for xcm-mapping (move to `types-known`)
|
||||
- Expose all extracted type names in `PortableRegistry`
|
||||
- Adjust `api.rpc.*.*.raw(..args)` return to the actual over-the-wire JSON result
|
||||
- Remove `api.rpc.*.*.json(..args)` since it now overlaps with new `.raw`
|
||||
|
||||
|
||||
## 6.4.2 Oct 18, 2021
|
||||
|
||||
@@ -366,8 +366,6 @@ export abstract class Decorate<ApiType extends ApiTypes> extends Events {
|
||||
if (this.hasSubscriptions || !(methodName.startsWith('subscribe') || methodName.startsWith('unsubscribe'))) {
|
||||
(section as Record<string, unknown>)[methodName] = decorateMethod(method, { methodName }) as unknown;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
(section as Record<string, { json: unknown }>)[methodName].json = decorateMethod(method.json, { methodName }) as unknown;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
(section as Record<string, { raw: unknown }>)[methodName].raw = decorateMethod(method.raw, { methodName }) as unknown;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
(section as Record<string, { meta: unknown }>)[methodName].meta = method.meta;
|
||||
@@ -622,9 +620,9 @@ export abstract class Decorate<ApiType extends ApiTypes> extends Events {
|
||||
return this._rpcCore.state
|
||||
.queryStorage<[Option<Raw>]>([decorated.key(...args)], ...range)
|
||||
.pipe(map((result: [Hash, [Option<Raw>]][]): [Hash, Codec][] =>
|
||||
result.map(([blockHash, [value]]): [Hash, Codec] => [
|
||||
result.map(([blockHash, [value]]) => [
|
||||
blockHash,
|
||||
this.createType(outputType, value.isSome ? value.unwrap().toHex() : undefined)
|
||||
this.createType<Codec>(outputType, value.isSome ? value.unwrap().toHex() : undefined)
|
||||
])
|
||||
));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable, Subscription } from 'rxjs';
|
||||
import type { RpcInterfaceMethod } from '@polkadot/rpc-core/types';
|
||||
import type { Text } from '@polkadot/types';
|
||||
import type { ChainProperties, Hash, RuntimeVersion } from '@polkadot/types/interfaces';
|
||||
import type { Registry } from '@polkadot/types/types';
|
||||
@@ -130,7 +129,7 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
|
||||
const header = this.registry.createType('HeaderPartial',
|
||||
this._genesisHash.eq(blockHash)
|
||||
? { number: BN_ZERO, parentHash: this._genesisHash }
|
||||
: await firstValueFrom((this._rpcCore.chain.getHeader as RpcInterfaceMethod).json(blockHash))
|
||||
: await firstValueFrom(this._rpcCore.chain.getHeader.raw(blockHash))
|
||||
);
|
||||
|
||||
assert(!header.parentHash.isEmpty, 'Unable to retrieve header and parent from supplied hash');
|
||||
@@ -140,11 +139,11 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
|
||||
const version = this.registry.createType('RuntimeVersionPartial',
|
||||
(firstVersion && (lastVersion || firstVersion.specVersion.eq(this._runtimeVersion.specVersion)))
|
||||
? { specName: this._runtimeVersion.specName, specVersion: firstVersion.specVersion }
|
||||
: await firstValueFrom((this._rpcCore.state.getRuntimeVersion as RpcInterfaceMethod).json(header.parentHash))
|
||||
: await firstValueFrom(this._rpcCore.state.getRuntimeVersion.raw(header.parentHash))
|
||||
);
|
||||
|
||||
// check for pre-existing registries. We also check specName, e.g. it
|
||||
// could be changed like in Westmint with upgrade from shell -> westmint
|
||||
// could be changed like in Westmint with upgrade from shell -> westmint
|
||||
const existingViaVersion = this.#registries.find(({ specName, specVersion }) =>
|
||||
specName.eq(version.specName) &&
|
||||
specVersion.eq(version.specVersion)
|
||||
@@ -159,7 +158,7 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
|
||||
// nothing has been found, construct new
|
||||
const registry = new TypeRegistry(blockHash);
|
||||
const metadata = new Metadata(registry,
|
||||
await firstValueFrom((this._rpcCore.state.getMetadata as RpcInterfaceMethod).raw(header.parentHash))
|
||||
await firstValueFrom(this._rpcCore.state.getMetadata.raw<string>(header.parentHash))
|
||||
);
|
||||
|
||||
this._initRegistry(registry, this._runtimeChain as Text, version, metadata);
|
||||
|
||||
@@ -182,7 +182,7 @@ export function detectedCapabilities (api: ApiInterfaceRx, blockHash?: Uint8Arra
|
||||
},
|
||||
extractResults<ExtractedC>(cResults, consts),
|
||||
extractResults<ExtractedQ>(qResults, queries),
|
||||
extractResults<ExtractedR>(rResults, raws)
|
||||
extractResults<ExtractedR>(rResults.map((r) => api.registry.createType('Raw', r)), raws)
|
||||
)
|
||||
),
|
||||
take(1),
|
||||
|
||||
@@ -177,12 +177,10 @@ async function rpc (api: ApiPromise): Promise<void> {
|
||||
});
|
||||
|
||||
// using json & raw
|
||||
await api.rpc.chain.getBlock.json('0x123456');
|
||||
await api.rpc.chain.getBlock.raw('0x123456');
|
||||
|
||||
// using raw subs
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
api.rpc.chain.subscribeNewHeads.raw((result: Uint8Array): void => {
|
||||
await api.rpc.chain.subscribeNewHeads.raw((result: Uint8Array): void => {
|
||||
console.log(result);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,23 +2,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Json, Raw } from '@polkadot/types/codec';
|
||||
import type { AnyFunction, Callback, DefinitionRpc } from '@polkadot/types/types';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { ApiTypes, PromiseResult, Push, RxResult, UnsubscribePromise } from './base';
|
||||
|
||||
export interface RpcRxResult<F extends AnyFunction> extends RxResult<F> {
|
||||
json (...args: Parameters<F>): Observable<Json>;
|
||||
raw (...args: Parameters<F>): Observable<Raw>;
|
||||
raw <T> (...args: Parameters<F>): Observable<T>;
|
||||
meta: DefinitionRpc;
|
||||
}
|
||||
|
||||
export interface RpcPromiseResult<F extends AnyFunction> extends PromiseResult<F> {
|
||||
json (...args: Parameters<F>): Promise<Json>;
|
||||
json (...args: Push<Parameters<F>, Callback<Json>>): UnsubscribePromise;
|
||||
raw (...args: Parameters<F>): Promise<Raw>;
|
||||
raw (...args: Push<Parameters<F>, Callback<Raw>>): UnsubscribePromise;
|
||||
raw <T> (...args: Parameters<F>): Promise<T>;
|
||||
raw <T> (...args: Push<Parameters<F>, Callback<T>>): UnsubscribePromise;
|
||||
meta: DefinitionRpc;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ interface StorageChangeSetJSON {
|
||||
changes: [string, string | null][];
|
||||
}
|
||||
|
||||
type OutputType = 'json' | 'raw' | 'scale';
|
||||
|
||||
const l = logger('rpc-core');
|
||||
|
||||
const EMPTY_META = {
|
||||
@@ -183,44 +181,46 @@ export class RpcCore {
|
||||
}, {} as RpcInterface[Section]);
|
||||
}
|
||||
|
||||
private _memomize (creator: <T> (outputAs: OutputType) => (...values: unknown[]) => Observable<T>, def: DefinitionRpc): Memoized<RpcInterfaceMethod> {
|
||||
const memoized = memoize(creator('scale') as RpcInterfaceMethod, {
|
||||
getInstanceId: () => this.#instanceId
|
||||
});
|
||||
private _memomize (creator: <T> (isScale: boolean) => (...values: unknown[]) => Observable<T>, def: DefinitionRpc): Memoized<RpcInterfaceMethod> {
|
||||
const memoOpts = { getInstanceId: () => this.#instanceId };
|
||||
const memoized = memoize(creator(true) as RpcInterfaceMethod, memoOpts);
|
||||
|
||||
memoized.json = creator('json');
|
||||
memoized.raw = creator('raw');
|
||||
memoized.raw = memoize(creator(false), memoOpts);
|
||||
memoized.meta = def;
|
||||
|
||||
return memoized;
|
||||
}
|
||||
|
||||
private _formatResult <T> (isScale: boolean, registry: Registry, blockHash: string | Uint8Array | null | undefined, method: string, def: DefinitionRpc, params: Codec[], result: unknown): T {
|
||||
return isScale
|
||||
? this._formatOutput(registry, blockHash, method, def, params, result) as unknown as T
|
||||
: result as T;
|
||||
}
|
||||
|
||||
private _createMethodSend (section: string, method: string, def: DefinitionRpc): RpcInterfaceMethod {
|
||||
const rpcName = def.endpoint || `${section}_${method}`;
|
||||
const hashIndex = def.params.findIndex(({ isHistoric }) => isHistoric);
|
||||
let memoized: null | Memoized<RpcInterfaceMethod> = null;
|
||||
|
||||
// execute the RPC call, doing a registry swap for historic as applicable
|
||||
const callWithRegistry = async <T> (outputAs: OutputType, values: unknown[]): Promise<T> => {
|
||||
const callWithRegistry = async <T> (isScale: boolean, values: unknown[]): Promise<T> => {
|
||||
const blockHash = hashIndex === -1
|
||||
? null
|
||||
: values[hashIndex] as (Uint8Array | string | null | undefined);
|
||||
const { registry } = outputAs === 'scale' && blockHash && this.#getBlockRegistry
|
||||
const { registry } = isScale && blockHash && this.#getBlockRegistry
|
||||
? await this.#getBlockRegistry(u8aToU8a(blockHash))
|
||||
: { registry: this.#registryDefault };
|
||||
const params = this._formatInputs(registry, null, def, values);
|
||||
const data = await this.provider.send<AnyJson>(rpcName, params.map((param) => param.toJSON()));
|
||||
const result = await this.provider.send<AnyJson>(rpcName, params.map((p) => p.toJSON()));
|
||||
|
||||
return outputAs === 'scale'
|
||||
? this._formatOutput(registry, blockHash, method, def, params, data) as unknown as T
|
||||
: registry.createType(outputAs === 'raw' ? 'Raw' : 'Json', data) as unknown as T;
|
||||
return this._formatResult(isScale, registry, blockHash, method, def, params, result);
|
||||
};
|
||||
|
||||
const creator = <T> (outputAs: OutputType) => (...values: unknown[]): Observable<T> => {
|
||||
const isDelayed = outputAs === 'scale' && hashIndex !== -1 && !!values[hashIndex];
|
||||
const creator = <T> (isScale: boolean) => (...values: unknown[]): Observable<T> => {
|
||||
const isDelayed = isScale && hashIndex !== -1 && !!values[hashIndex];
|
||||
|
||||
return new Observable((observer: Observer<T>): VoidCallback => {
|
||||
callWithRegistry<T>(outputAs, values)
|
||||
callWithRegistry<T>(isScale, values)
|
||||
.then((value): void => {
|
||||
observer.next(value);
|
||||
observer.complete();
|
||||
@@ -269,7 +269,7 @@ export class RpcCore {
|
||||
const subType = `${section}_${updateType}`;
|
||||
let memoized: null | Memoized<RpcInterfaceMethod> = null;
|
||||
|
||||
const creator = <T> (outputAs: OutputType) => (...values: unknown[]): Observable<T> => {
|
||||
const creator = <T> (isScale: boolean) => (...values: unknown[]): Observable<T> => {
|
||||
return new Observable((observer: Observer<T>): VoidCallback => {
|
||||
// Have at least an empty promise, as used in the unsubscribe
|
||||
let subscriptionPromise: Promise<number | string | null> = Promise.resolve(null);
|
||||
@@ -283,9 +283,9 @@ export class RpcCore {
|
||||
|
||||
try {
|
||||
const params = this._formatInputs(registry, null, def, values);
|
||||
const paramsJson = params.map((param) => param.toJSON());
|
||||
const paramsJson = params.map((p) => p.toJSON());
|
||||
|
||||
const update = (error?: Error | null, result?: T): void => {
|
||||
const update = (error?: Error | null, result?: unknown): void => {
|
||||
if (error) {
|
||||
logErrorMessage(method, def, error);
|
||||
|
||||
@@ -293,11 +293,7 @@ export class RpcCore {
|
||||
}
|
||||
|
||||
try {
|
||||
observer.next(
|
||||
outputAs === 'scale'
|
||||
? this._formatOutput(registry, null, method, def, params, result) as unknown as T
|
||||
: registry.createType(outputAs === 'raw' ? 'Raw' : 'Json', result) as unknown as T
|
||||
);
|
||||
observer.next(this._formatResult(isScale, registry, null, method, def, params, result));
|
||||
} catch (error) {
|
||||
observer.error(error);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
|
||||
// FIXME, this whole file needs to move to API
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { AnyFunction } from '@polkadot/types/types';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface RpcInterface {}
|
||||
|
||||
export type AugmentedRpc<F extends AnyFunction> = F;
|
||||
export type AugmentedRpc<F extends AnyFunction> = F & {
|
||||
raw: <T> (...params: Parameters<F>) => Observable<T>;
|
||||
};
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Json, Raw } from '@polkadot/types';
|
||||
import type { Codec, DefinitionRpc } from '@polkadot/types/types';
|
||||
|
||||
export * from './types.jsonrpc';
|
||||
|
||||
export interface RpcInterfaceMethod {
|
||||
<T extends Codec> (...params: unknown[]): Observable<T>;
|
||||
json (...params: unknown[]): Observable<Json>;
|
||||
raw (...params: unknown[]): Observable<Raw>;
|
||||
raw (...params: unknown[]): Observable<unknown>;
|
||||
meta: DefinitionRpc;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ export function getUpgradeVersion (genesisHash: Hash, blockNumber: BN): [ChainUp
|
||||
|
||||
return known
|
||||
? [
|
||||
known.versions.reduce((last: ChainUpgradeVersion | undefined, version): ChainUpgradeVersion | undefined => {
|
||||
known.versions.reduce<ChainUpgradeVersion | undefined>((last, version) => {
|
||||
return blockNumber.gt(version.blockNumber)
|
||||
? version
|
||||
: last;
|
||||
|
||||
Reference in New Issue
Block a user