From d7b727f818618da49834474c3517fca98c966385 Mon Sep 17 00:00:00 2001 From: tarikgul Date: Thu, 18 Jul 2024 22:55:01 -0400 Subject: [PATCH] Initialize the API with metadata V15 --- packages/api/src/base/Init.ts | 51 +++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/packages/api/src/base/Init.ts b/packages/api/src/base/Init.ts index face20e709c7..c7ad10403f0e 100644 --- a/packages/api/src/base/Init.ts +++ b/packages/api/src/base/Init.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { Observable, Subscription } from 'rxjs'; -import type { Text } from '@polkadot/types'; +import type { Text, u32 } from '@polkadot/types'; import type { ExtDef } from '@polkadot/types/extrinsic/signedExtensions/types'; import type { ChainProperties, Hash, HeaderPartial, RuntimeVersion, RuntimeVersionPartial } from '@polkadot/types/interfaces'; import type { Registry } from '@polkadot/types/types'; @@ -125,6 +125,9 @@ export abstract class Init extends Decorate { private async _createBlockRegistry (blockHash: Uint8Array, header: HeaderPartial, version: RuntimeVersionPartial): Promise> { const registry = new TypeRegistry(blockHash); + + console.log('hit _createBlockRegistry'); + console.log('keys of _call: ', Object.keys(this._call.core)); const metadata = new Metadata(registry, await firstValueFrom(this._rpcCore.state.getMetadata.raw(header.parentHash)) ); @@ -330,15 +333,12 @@ export abstract class Init extends Decorate { } private async _metaFromChain (optMetadata?: Record): Promise<[Hash, Metadata]> { - const [genesisHash, runtimeVersion, chain, chainProps, rpcMethods, chainMetadata] = await Promise.all([ + const [genesisHash, runtimeVersion, chain, chainProps, rpcMethods] = await Promise.all([ firstValueFrom(this._rpcCore.chain.getBlockHash(0)), firstValueFrom(this._rpcCore.state.getRuntimeVersion()), firstValueFrom(this._rpcCore.system.chain()), firstValueFrom(this._rpcCore.system.properties()), - firstValueFrom(this._rpcCore.rpc.methods()), - optMetadata - ? Promise.resolve(null) - : firstValueFrom(this._rpcCore.state.getMetadata()) + firstValueFrom(this._rpcCore.rpc.methods()) ]); // set our chain version & genesisHash as returned @@ -348,11 +348,9 @@ export abstract class Init extends Decorate { // retrieve metadata, either from chain or as pass-in via options const metadataKey = `${genesisHash.toHex() || '0x'}-${runtimeVersion.specVersion.toString()}`; - const metadata = chainMetadata || ( - optMetadata?.[metadataKey] - ? new Metadata(this.registry, optMetadata[metadataKey]) - : await firstValueFrom(this._rpcCore.state.getMetadata()) - ); + const metadata = optMetadata?.[metadataKey] + ? new Metadata(this.registry, optMetadata[metadataKey]) + : await this._retrieveMetadata(); // initializes the registry & RPC this._initRegistry(this.registry, chain, runtimeVersion, metadata, chainProps); @@ -392,6 +390,37 @@ export abstract class Init extends Decorate { return true; } + /** + * @internal + * + * Tries to use runtime api calls to retrieve metadata. This ensures the api initializes with the latest metadata. + * If the runtime call is not there it will use the rpc method. + */ + private async _retrieveMetadata (): Promise { + // TODO increase the safety of this call by checking the runtime api hashes to ensure metadata is there. + let metadataVersion: u32 | null = null; + + try { + const metadataVersionsAsBytes = await firstValueFrom(this._rpcCore.state.call('Metadata_metadata_versions', '0x')); + const versions = this.registry.createType('Vec', metadataVersionsAsBytes); + + metadataVersion = versions.reduce((largest, current) => current.gt(largest) ? current : largest); + } catch { + l.warn('API-INIT: state_call::Metadata_metadata_versions not available, rpc::state::get_metadata will be used.'); + } + + if (metadataVersion) { + const metadataBytes = await firstValueFrom(this._rpcCore.state.call('Metadata_metadata_at_version', u8aToHex(metadataVersion.toU8a()))); + const opaqueMetadata = this.registry.createType('Option', metadataBytes).unwrapOr(null); + + if (opaqueMetadata) { + return new Metadata(this.registry, opaqueMetadata.toHex()); + } + } + + return await firstValueFrom(this._rpcCore.state.getMetadata()); + } + private _subscribeHealth (): void { this._unsubscribeHealth();