PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo/node_modules/@polkadot/rpc-provider/substrate-connect

Просмотр файла: index.js

import { EventEmitter } from 'eventemitter3';
import { isError, isFunction, isObject, logger, noop, objectSpread } from '@polkadot/util';
import { RpcCoder } from '../coder/index.js';
import { healthChecker } from './Health.js';
const l = logger('api-substrate-connect');
const subscriptionUnsubscriptionMethods = new Map([
    ['author_submitAndWatchExtrinsic', 'author_unwatchExtrinsic'],
    ['chain_subscribeAllHeads', 'chain_unsubscribeAllHeads'],
    ['chain_subscribeFinalizedHeads', 'chain_unsubscribeFinalizedHeads'],
    ['chain_subscribeFinalisedHeads', 'chain_subscribeFinalisedHeads'],
    ['chain_subscribeNewHeads', 'chain_unsubscribeNewHeads'],
    ['chain_subscribeNewHead', 'chain_unsubscribeNewHead'],
    ['chain_subscribeRuntimeVersion', 'chain_unsubscribeRuntimeVersion'],
    ['subscribe_newHead', 'unsubscribe_newHead'],
    ['state_subscribeRuntimeVersion', 'state_unsubscribeRuntimeVersion'],
    ['state_subscribeStorage', 'state_unsubscribeStorage']
]);
const scClients = new WeakMap();
export class ScProvider {
    __internal__Sc;
    __internal__coder = new RpcCoder();
    __internal__spec;
    __internal__sharedSandbox;
    __internal__subscriptions = new Map();
    __internal__resubscribeMethods = new Map();
    __internal__requests = new Map();
    __internal__wellKnownChains;
    __internal__eventemitter = new EventEmitter();
    __internal__chain = null;
    __internal__isChainReady = false;
    constructor(Sc, spec, sharedSandbox) {
        if (!isObject(Sc) || !isObject(Sc.WellKnownChain) || !isFunction(Sc.createScClient)) {
            throw new Error('Expected an @substrate/connect interface as first parameter to ScProvider');
        }
        this.__internal__Sc = Sc;
        this.__internal__spec = spec;
        this.__internal__sharedSandbox = sharedSandbox;
        this.__internal__wellKnownChains = new Set(Object.values(Sc.WellKnownChain));
    }
    get hasSubscriptions() {
        // Indicates that subscriptions are supported
        return !!true;
    }
    get isClonable() {
        return !!false;
    }
    get isConnected() {
        return !!this.__internal__chain && this.__internal__isChainReady;
    }
    clone() {
        throw new Error('clone() is not supported.');
    }
    // Config details can be found in @substrate/connect repo following the link:
    // https://github.com/paritytech/substrate-connect/blob/main/packages/connect/src/connector/index.ts
    async connect(config, checkerFactory = healthChecker) {
        if (this.isConnected) {
            throw new Error('Already connected!');
        }
        // it could happen that after emitting `disconnected` due to the fact that
        // smoldot is syncing, the consumer tries to reconnect after a certain amount
        // of time... In which case we want to make sure that we don't create a new
        // chain.
        if (this.__internal__chain) {
            await this.__internal__chain;
            return;
        }
        if (this.__internal__sharedSandbox && !this.__internal__sharedSandbox.isConnected) {
            await this.__internal__sharedSandbox.connect();
        }
        const client = this.__internal__sharedSandbox
            ? scClients.get(this.__internal__sharedSandbox)
            : this.__internal__Sc.createScClient(config);
        if (!client) {
            throw new Error('Unknown ScProvider!');
        }
        scClients.set(this, client);
        const hc = checkerFactory();
        const onResponse = (res) => {
            const hcRes = hc.responsePassThrough(res);
            if (!hcRes) {
                return;
            }
            const response = JSON.parse(hcRes);
            let decodedResponse;
            try {
                decodedResponse = this.__internal__coder.decodeResponse(response);
            }
            catch (e) {
                decodedResponse = e;
            }
            // It's not a subscription message, but rather a standar RPC response
            if (response.params?.subscription === undefined || !response.method) {
                return this.__internal__requests.get(response.id)?.(decodedResponse);
            }
            // We are dealing with a subscription message
            const subscriptionId = `${response.method}::${response.params.subscription}`;
            const callback = this.__internal__subscriptions.get(subscriptionId)?.[0];
            callback?.(decodedResponse);
        };
        const addChain = this.__internal__sharedSandbox
            ? (async (...args) => {
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                const source = this.__internal__sharedSandbox;
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                return (await source.__internal__chain).addChain(...args);
            })
            : this.__internal__wellKnownChains.has(this.__internal__spec)
                ? client.addWellKnownChain
                : client.addChain;
        this.__internal__chain = addChain(this.__internal__spec, onResponse).then((chain) => {
            hc.setSendJsonRpc(chain.sendJsonRpc);
            this.__internal__isChainReady = false;
            const cleanup = () => {
                // If there are any callbacks left, we have to reject/error them.
                // Otherwise, that would cause a memory leak.
                const disconnectionError = new Error('Disconnected');
                this.__internal__requests.forEach((cb) => cb(disconnectionError));
                this.__internal__subscriptions.forEach(([cb]) => cb(disconnectionError));
                this.__internal__subscriptions.clear();
            };
            const staleSubscriptions = [];
            const killStaleSubscriptions = () => {
                if (staleSubscriptions.length === 0) {
                    return;
                }
                const stale = staleSubscriptions.pop();
                if (!stale) {
                    throw new Error('Unable to get stale subscription');
                }
                const { id, unsubscribeMethod } = stale;
                Promise
                    .race([
                    this.send(unsubscribeMethod, [id]).catch(noop),
                    new Promise((resolve) => setTimeout(resolve, 500))
                ])
                    .then(killStaleSubscriptions)
                    .catch(noop);
            };
            hc.start((health) => {
                const isReady = !health.isSyncing && (health.peers > 0 || !health.shouldHavePeers);
                // if it's the same as before, then nothing has changed and we are done
                if (this.__internal__isChainReady === isReady) {
                    return;
                }
                this.__internal__isChainReady = isReady;
                if (!isReady) {
                    // If we've reached this point, that means that the chain used to be "ready"
                    // and now we are about to emit `disconnected`.
                    //
                    // This will cause the PolkadotJs API think that the connection is
                    // actually dead. In reality the smoldot chain is not dead, of course.
                    // However, we have to cleanup all the existing callbacks because when
                    // the smoldot chain stops syncing, then we will emit `connected` and
                    // the PolkadotJs API will try to re-create the previous
                    // subscriptions and requests. Although, now is not a good moment
                    // to be sending unsubscription messages to the smoldot chain, we
                    // should wait until is no longer syncing to send the unsubscription
                    // messages from the stale subscriptions of the previous connection.
                    //
                    // That's why -before we perform the cleanup of `this.__internal__subscriptions`-
                    // we keep the necessary information that we will need later on to
                    // kill the stale subscriptions.
                    [...this.__internal__subscriptions.values()].forEach((s) => {
                        staleSubscriptions.push(s[1]);
                    });
                    cleanup();
                    this.__internal__eventemitter.emit('disconnected');
                }
                else {
                    killStaleSubscriptions();
                    this.__internal__eventemitter.emit('connected');
                    if (this.__internal__resubscribeMethods.size) {
                        this.__internal__resubscribe();
                    }
                }
            });
            return objectSpread({}, chain, {
                remove: () => {
                    hc.stop();
                    chain.remove();
                    cleanup();
                },
                sendJsonRpc: hc.sendJsonRpc.bind(hc)
            });
        });
        try {
            await this.__internal__chain;
        }
        catch (e) {
            this.__internal__chain = null;
            this.__internal__eventemitter.emit('error', e);
            throw e;
        }
    }
    __internal__resubscribe = () => {
        const promises = [];
        this.__internal__resubscribeMethods.forEach((subDetails) => {
            // only re-create subscriptions which are not in author (only area where
            // transactions are created, i.e. submissions such as 'author_submitAndWatchExtrinsic'
            // are not included (and will not be re-broadcast)
            if (subDetails.type.startsWith('author_')) {
                return;
            }
            try {
                const promise = new Promise((resolve) => {
                    this.subscribe(subDetails.type, subDetails.method, subDetails.params, subDetails.callback).catch((error) => console.log(error));
                    resolve();
                });
                promises.push(promise);
            }
            catch (error) {
                l.error(error);
            }
        });
        Promise.all(promises).catch((err) => l.log(err));
    };
    async disconnect() {
        if (!this.__internal__chain) {
            return;
        }
        const chain = await this.__internal__chain;
        this.__internal__chain = null;
        this.__internal__isChainReady = false;
        try {
            chain.remove();
        }
        catch (_) { }
        this.__internal__eventemitter.emit('disconnected');
    }
    on(type, sub) {
        // It's possible. Although, quite unlikely, that by the time that polkadot
        // subscribes to the `connected` event, the Provider is already connected.
        // In that case, we must emit to let the consumer know that we are connected.
        if (type === 'connected' && this.isConnected) {
            sub();
        }
        this.__internal__eventemitter.on(type, sub);
        return () => {
            this.__internal__eventemitter.removeListener(type, sub);
        };
    }
    async send(method, params) {
        if (!this.isConnected || !this.__internal__chain) {
            throw new Error('Provider is not connected');
        }
        const chain = await this.__internal__chain;
        const [id, json] = this.__internal__coder.encodeJson(method, params);
        const result = new Promise((resolve, reject) => {
            this.__internal__requests.set(id, (response) => {
                (isError(response) ? reject : resolve)(response);
            });
            try {
                chain.sendJsonRpc(json);
            }
            catch (e) {
                this.__internal__chain = null;
                try {
                    chain.remove();
                }
                catch (_) { }
                this.__internal__eventemitter.emit('error', e);
            }
        });
        try {
            return await result;
        }
        finally {
            // let's ensure that once the Promise is resolved/rejected, then we remove
            // remove its entry from the internal #requests
            this.__internal__requests.delete(id);
        }
    }
    async subscribe(type, method, params, callback) {
        if (!subscriptionUnsubscriptionMethods.has(method)) {
            throw new Error(`Unsupported subscribe method: ${method}`);
        }
        const id = await this.send(method, params);
        const subscriptionId = `${type}::${id}`;
        const cb = (response) => {
            if (response instanceof Error) {
                callback(response, undefined);
            }
            else {
                callback(null, response);
            }
        };
        const unsubscribeMethod = subscriptionUnsubscriptionMethods.get(method);
        if (!unsubscribeMethod) {
            throw new Error('Invalid unsubscribe method found');
        }
        this.__internal__resubscribeMethods.set(subscriptionId, { callback, method, params, type });
        this.__internal__subscriptions.set(subscriptionId, [cb, { id, unsubscribeMethod }]);
        return id;
    }
    unsubscribe(type, method, id) {
        if (!this.isConnected) {
            throw new Error('Provider is not connected');
        }
        const subscriptionId = `${type}::${id}`;
        if (!this.__internal__subscriptions.has(subscriptionId)) {
            return Promise.reject(new Error(`Unable to find active subscription=${subscriptionId}`));
        }
        this.__internal__resubscribeMethods.delete(subscriptionId);
        this.__internal__subscriptions.delete(subscriptionId);
        return this.send(method, [id]);
    }
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!