1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.Init = void 0;
|
4 | const rxjs_1 = require("rxjs");
|
5 | const types_1 = require("@polkadot/types");
|
6 | const types_known_1 = require("@polkadot/types-known");
|
7 | const util_1 = require("@polkadot/util");
|
8 | const util_crypto_1 = require("@polkadot/util-crypto");
|
9 | const Decorate_js_1 = require("./Decorate.js");
|
10 | const KEEPALIVE_INTERVAL = 10000;
|
11 | const WITH_VERSION_SHORTCUT = false;
|
12 | const l = (0, util_1.logger)('api/init');
|
13 | function textToString(t) {
|
14 | return t.toString();
|
15 | }
|
16 | class Init extends Decorate_js_1.Decorate {
|
17 | __internal__atLast = null;
|
18 | __internal__healthTimer = null;
|
19 | __internal__registries = [];
|
20 | __internal__updateSub = null;
|
21 | __internal__waitingRegistries = {};
|
22 | constructor(options, type, decorateMethod) {
|
23 | super(options, type, decorateMethod);
|
24 |
|
25 | this.registry.setKnownTypes(options);
|
26 |
|
27 |
|
28 |
|
29 | if (!options.source) {
|
30 | this.registerTypes(options.types);
|
31 | }
|
32 | else {
|
33 | this.__internal__registries = options.source.__internal__registries;
|
34 | }
|
35 | this._rpc = this._decorateRpc(this._rpcCore, this._decorateMethod);
|
36 | this._rx.rpc = this._decorateRpc(this._rpcCore, this._rxDecorateMethod);
|
37 | if (this.supportMulti) {
|
38 | this._queryMulti = this._decorateMulti(this._decorateMethod);
|
39 | this._rx.queryMulti = this._decorateMulti(this._rxDecorateMethod);
|
40 | }
|
41 | this._rx.signer = options.signer;
|
42 | this._rpcCore.setRegistrySwap((blockHash) => this.getBlockRegistry(blockHash));
|
43 | this._rpcCore.setResolveBlockHash((blockNumber) => (0, rxjs_1.firstValueFrom)(this._rpcCore.chain.getBlockHash(blockNumber)));
|
44 | if (this.hasSubscriptions) {
|
45 | this._rpcCore.provider.on('disconnected', () => this.__internal__onProviderDisconnect());
|
46 | this._rpcCore.provider.on('error', (e) => this.__internal__onProviderError(e));
|
47 | this._rpcCore.provider.on('connected', () => this.__internal__onProviderConnect());
|
48 | }
|
49 | else if (!this._options.noInitWarn) {
|
50 | l.warn('Api will be available in a limited mode since the provider does not support subscriptions');
|
51 | }
|
52 |
|
53 |
|
54 |
|
55 | if (this._rpcCore.provider.isConnected) {
|
56 | this.__internal__onProviderConnect().catch(util_1.noop);
|
57 | }
|
58 | }
|
59 | |
60 |
|
61 |
|
62 | _initRegistry(registry, chain, version, metadata, chainProps) {
|
63 | registry.clearCache();
|
64 | registry.setChainProperties(chainProps || this.registry.getChainProperties());
|
65 | registry.setKnownTypes(this._options);
|
66 | registry.register((0, types_known_1.getSpecTypes)(registry, chain, version.specName, version.specVersion));
|
67 | registry.setHasher((0, types_known_1.getSpecHasher)(registry, chain, version.specName));
|
68 |
|
69 | if (registry.knownTypes.typesBundle) {
|
70 | registry.knownTypes.typesAlias = (0, types_known_1.getSpecAlias)(registry, chain, version.specName);
|
71 | }
|
72 | registry.setMetadata(metadata, undefined, (0, util_1.objectSpread)({}, (0, types_known_1.getSpecExtensions)(registry, chain, version.specName), this._options.signedExtensions), this._options.noInitWarn);
|
73 | }
|
74 | |
75 |
|
76 |
|
77 | _getDefaultRegistry() {
|
78 | return (0, util_1.assertReturn)(this.__internal__registries.find(({ isDefault }) => isDefault), 'Initialization error, cannot find the default registry');
|
79 | }
|
80 | |
81 |
|
82 |
|
83 | async at(blockHash, knownVersion) {
|
84 | const u8aHash = (0, util_1.u8aToU8a)(blockHash);
|
85 | const u8aHex = (0, util_1.u8aToHex)(u8aHash);
|
86 | const registry = await this.getBlockRegistry(u8aHash, knownVersion);
|
87 | if (!this.__internal__atLast || this.__internal__atLast[0] !== u8aHex) {
|
88 |
|
89 |
|
90 | this.__internal__atLast = [u8aHex, this._createDecorated(registry, true, null, u8aHash).decoratedApi];
|
91 | }
|
92 | return this.__internal__atLast[1];
|
93 | }
|
94 | async _createBlockRegistry(blockHash, header, version) {
|
95 | const registry = new types_1.TypeRegistry(blockHash);
|
96 | const metadata = new types_1.Metadata(registry, await (0, rxjs_1.firstValueFrom)(this._rpcCore.state.getMetadata.raw(header.parentHash)));
|
97 | const runtimeChain = this._runtimeChain;
|
98 | if (!runtimeChain) {
|
99 | throw new Error('Invalid initializion order, runtimeChain is not available');
|
100 | }
|
101 | this._initRegistry(registry, runtimeChain, version, metadata);
|
102 |
|
103 | const result = { counter: 0, lastBlockHash: blockHash, metadata, registry, runtimeVersion: version };
|
104 | this.__internal__registries.push(result);
|
105 | return result;
|
106 | }
|
107 | _cacheBlockRegistryProgress(key, creator) {
|
108 |
|
109 | let waiting = this.__internal__waitingRegistries[key];
|
110 | if ((0, util_1.isUndefined)(waiting)) {
|
111 |
|
112 | waiting = this.__internal__waitingRegistries[key] = new Promise((resolve, reject) => {
|
113 | creator()
|
114 | .then((registry) => {
|
115 | delete this.__internal__waitingRegistries[key];
|
116 | resolve(registry);
|
117 | })
|
118 | .catch((error) => {
|
119 | delete this.__internal__waitingRegistries[key];
|
120 | reject(error);
|
121 | });
|
122 | });
|
123 | }
|
124 | return waiting;
|
125 | }
|
126 | _getBlockRegistryViaVersion(blockHash, version) {
|
127 | if (version) {
|
128 |
|
129 |
|
130 | const existingViaVersion = this.__internal__registries.find(({ runtimeVersion: { specName, specVersion } }) => specName.eq(version.specName) &&
|
131 | specVersion.eq(version.specVersion));
|
132 | if (existingViaVersion) {
|
133 | existingViaVersion.counter++;
|
134 | existingViaVersion.lastBlockHash = blockHash;
|
135 | return existingViaVersion;
|
136 | }
|
137 | }
|
138 | return null;
|
139 | }
|
140 | async _getBlockRegistryViaHash(blockHash) {
|
141 |
|
142 | if (!this._genesisHash || !this._runtimeVersion) {
|
143 | throw new Error('Cannot retrieve data on an uninitialized chain');
|
144 | }
|
145 |
|
146 |
|
147 | const header = this.registry.createType('HeaderPartial', this._genesisHash.eq(blockHash)
|
148 | ? { number: util_1.BN_ZERO, parentHash: this._genesisHash }
|
149 | : await (0, rxjs_1.firstValueFrom)(this._rpcCore.chain.getHeader.raw(blockHash)));
|
150 | if (header.parentHash.isEmpty) {
|
151 | throw new Error('Unable to retrieve header and parent from supplied hash');
|
152 | }
|
153 |
|
154 | const [firstVersion, lastVersion] = (0, types_known_1.getUpgradeVersion)(this._genesisHash, header.number);
|
155 | const version = this.registry.createType('RuntimeVersionPartial', WITH_VERSION_SHORTCUT && (firstVersion && (lastVersion ||
|
156 | firstVersion.specVersion.eq(this._runtimeVersion.specVersion)))
|
157 | ? { apis: firstVersion.apis, specName: this._runtimeVersion.specName, specVersion: firstVersion.specVersion }
|
158 | : await (0, rxjs_1.firstValueFrom)(this._rpcCore.state.getRuntimeVersion.raw(header.parentHash)));
|
159 | return (
|
160 |
|
161 | this._getBlockRegistryViaVersion(blockHash, version) ||
|
162 |
|
163 | await this._cacheBlockRegistryProgress(version.toHex(), () => this._createBlockRegistry(blockHash, header, version)));
|
164 | }
|
165 | |
166 |
|
167 |
|
168 | async getBlockRegistry(blockHash, knownVersion) {
|
169 | return (
|
170 |
|
171 | this.__internal__registries.find(({ lastBlockHash }) => lastBlockHash && (0, util_1.u8aEq)(lastBlockHash, blockHash)) ||
|
172 |
|
173 | this._getBlockRegistryViaVersion(blockHash, knownVersion) ||
|
174 |
|
175 | await this._cacheBlockRegistryProgress((0, util_1.u8aToHex)(blockHash), () => this._getBlockRegistryViaHash(blockHash)));
|
176 | }
|
177 | async _loadMeta() {
|
178 |
|
179 | if (this._isReady) {
|
180 | return true;
|
181 | }
|
182 | this._unsubscribeUpdates();
|
183 |
|
184 |
|
185 | [this._genesisHash, this._runtimeMetadata] = this._options.source?._isReady
|
186 | ? await this._metaFromSource(this._options.source)
|
187 | : await this._metaFromChain(this._options.metadata);
|
188 | return this._initFromMeta(this._runtimeMetadata);
|
189 | }
|
190 |
|
191 | async _metaFromSource(source) {
|
192 | this._extrinsicType = source.extrinsicVersion;
|
193 | this._runtimeChain = source.runtimeChain;
|
194 | this._runtimeVersion = source.runtimeVersion;
|
195 |
|
196 |
|
197 | const sections = Object.keys(source.rpc);
|
198 | const rpcs = [];
|
199 | for (let s = 0, scount = sections.length; s < scount; s++) {
|
200 | const section = sections[s];
|
201 | const methods = Object.keys(source.rpc[section]);
|
202 | for (let m = 0, mcount = methods.length; m < mcount; m++) {
|
203 | rpcs.push(`${section}_${methods[m]}`);
|
204 | }
|
205 | }
|
206 | this._filterRpc(rpcs, (0, types_known_1.getSpecRpc)(this.registry, source.runtimeChain, source.runtimeVersion.specName));
|
207 | return [source.genesisHash, source.runtimeMetadata];
|
208 | }
|
209 |
|
210 | _subscribeUpdates() {
|
211 | if (this.__internal__updateSub || !this.hasSubscriptions) {
|
212 | return;
|
213 | }
|
214 | this.__internal__updateSub = this._rpcCore.state.subscribeRuntimeVersion().pipe((0, rxjs_1.switchMap)((version) =>
|
215 |
|
216 | this._runtimeVersion?.specVersion.eq(version.specVersion)
|
217 | ? (0, rxjs_1.of)(false)
|
218 | : this._rpcCore.state.getMetadata().pipe((0, rxjs_1.map)((metadata) => {
|
219 | l.log(`Runtime version updated to spec=${version.specVersion.toString()}, tx=${version.transactionVersion.toString()}`);
|
220 | this._runtimeMetadata = metadata;
|
221 | this._runtimeVersion = version;
|
222 | this._rx.runtimeVersion = version;
|
223 |
|
224 | const thisRegistry = this._getDefaultRegistry();
|
225 | const runtimeChain = this._runtimeChain;
|
226 | if (!runtimeChain) {
|
227 | throw new Error('Invalid initializion order, runtimeChain is not available');
|
228 | }
|
229 |
|
230 | thisRegistry.metadata = metadata;
|
231 | thisRegistry.runtimeVersion = version;
|
232 | this._initRegistry(this.registry, runtimeChain, version, metadata);
|
233 | this._injectMetadata(thisRegistry, true);
|
234 | return true;
|
235 | })))).subscribe();
|
236 | }
|
237 | async _metaFromChain(optMetadata) {
|
238 | const [genesisHash, runtimeVersion, chain, chainProps, rpcMethods, chainMetadata] = await Promise.all([
|
239 | (0, rxjs_1.firstValueFrom)(this._rpcCore.chain.getBlockHash(0)),
|
240 | (0, rxjs_1.firstValueFrom)(this._rpcCore.state.getRuntimeVersion()),
|
241 | (0, rxjs_1.firstValueFrom)(this._rpcCore.system.chain()),
|
242 | (0, rxjs_1.firstValueFrom)(this._rpcCore.system.properties()),
|
243 | (0, rxjs_1.firstValueFrom)(this._rpcCore.rpc.methods()),
|
244 | optMetadata
|
245 | ? Promise.resolve(null)
|
246 | : (0, rxjs_1.firstValueFrom)(this._rpcCore.state.getMetadata())
|
247 | ]);
|
248 |
|
249 | this._runtimeChain = chain;
|
250 | this._runtimeVersion = runtimeVersion;
|
251 | this._rx.runtimeVersion = runtimeVersion;
|
252 |
|
253 | const metadataKey = `${genesisHash.toHex() || '0x'}-${runtimeVersion.specVersion.toString()}`;
|
254 | const metadata = chainMetadata || (optMetadata?.[metadataKey]
|
255 | ? new types_1.Metadata(this.registry, optMetadata[metadataKey])
|
256 | : await (0, rxjs_1.firstValueFrom)(this._rpcCore.state.getMetadata()));
|
257 |
|
258 | this._initRegistry(this.registry, chain, runtimeVersion, metadata, chainProps);
|
259 | this._filterRpc(rpcMethods.methods.map(textToString), (0, types_known_1.getSpecRpc)(this.registry, chain, runtimeVersion.specName));
|
260 | this._subscribeUpdates();
|
261 |
|
262 | if (!this.__internal__registries.length) {
|
263 | this.__internal__registries.push({ counter: 0, isDefault: true, metadata, registry: this.registry, runtimeVersion });
|
264 | }
|
265 |
|
266 | metadata.getUniqTypes(this._options.throwOnUnknown || false);
|
267 | return [genesisHash, metadata];
|
268 | }
|
269 | _initFromMeta(metadata) {
|
270 | const runtimeVersion = this._runtimeVersion;
|
271 | if (!runtimeVersion) {
|
272 | throw new Error('Invalid initializion order, runtimeVersion is not available');
|
273 | }
|
274 | this._extrinsicType = metadata.asLatest.extrinsic.version.toNumber();
|
275 | this._rx.extrinsicType = this._extrinsicType;
|
276 | this._rx.genesisHash = this._genesisHash;
|
277 | this._rx.runtimeVersion = runtimeVersion;
|
278 |
|
279 | this._injectMetadata(this._getDefaultRegistry(), true);
|
280 |
|
281 | this._rx.derive = this._decorateDeriveRx(this._rxDecorateMethod);
|
282 | this._derive = this._decorateDerive(this._decorateMethod);
|
283 | return true;
|
284 | }
|
285 | _subscribeHealth() {
|
286 | this._unsubscribeHealth();
|
287 |
|
288 | this.__internal__healthTimer = this.hasSubscriptions
|
289 | ? setInterval(() => {
|
290 | (0, rxjs_1.firstValueFrom)(this._rpcCore.system.health.raw()).catch(util_1.noop);
|
291 | }, KEEPALIVE_INTERVAL)
|
292 | : null;
|
293 | }
|
294 | _unsubscribeHealth() {
|
295 | if (this.__internal__healthTimer) {
|
296 | clearInterval(this.__internal__healthTimer);
|
297 | this.__internal__healthTimer = null;
|
298 | }
|
299 | }
|
300 | _unsubscribeUpdates() {
|
301 | if (this.__internal__updateSub) {
|
302 | this.__internal__updateSub.unsubscribe();
|
303 | this.__internal__updateSub = null;
|
304 | }
|
305 | }
|
306 | _unsubscribe() {
|
307 | this._unsubscribeHealth();
|
308 | this._unsubscribeUpdates();
|
309 | }
|
310 | async __internal__onProviderConnect() {
|
311 | this._isConnected.next(true);
|
312 | this.emit('connected');
|
313 | try {
|
314 | const cryptoReady = this._options.initWasm === false
|
315 | ? true
|
316 | : await (0, util_crypto_1.cryptoWaitReady)();
|
317 | const hasMeta = await this._loadMeta();
|
318 | this._subscribeHealth();
|
319 | if (hasMeta && !this._isReady && cryptoReady) {
|
320 | this._isReady = true;
|
321 | this.emit('ready', this);
|
322 | }
|
323 | }
|
324 | catch (_error) {
|
325 | const error = new Error(`FATAL: Unable to initialize the API: ${_error.message}`);
|
326 | l.error(error);
|
327 | this.emit('error', error);
|
328 | }
|
329 | }
|
330 | __internal__onProviderDisconnect() {
|
331 | this._isConnected.next(false);
|
332 | this._unsubscribe();
|
333 | this.emit('disconnected');
|
334 | }
|
335 | __internal__onProviderError(error) {
|
336 | this.emit('error', error);
|
337 | }
|
338 | }
|
339 | exports.Init = Init;
|