PHP WebShell

Текущая директория: /opt/BitGoJS/node_modules/@substrate/light-client-extension-helpers/dist/background

Просмотр файла: background-helper.mjs

import {
  supervise
} from "../chunk-INDV5VY4.mjs";
import {
  get,
  getChains,
  onChainsChanged,
  remove,
  set
} from "../chunk-TMDP3M7I.mjs";
import {
  ALARM,
  PORT,
  isSubstrateConnectToExtensionMessage
} from "../chunk-FJY652KW.mjs";
import {
  RpcError,
  createRpc,
  isRpcMessage
} from "../chunk-KOQEJYB7.mjs";

// src/background/background-helper.ts
import {
  createClient
} from "@polkadot-api/substrate-client";
import { getObservableClient } from "@polkadot-api/observable-client";
import { getSyncProvider as getSyncProvider2 } from "@polkadot-api/json-rpc-provider-proxy";
import {
  Observable,
  firstValueFrom,
  catchError,
  defer,
  EMPTY,
  repeat
} from "rxjs";

// src/background/json-rpc-provider.ts
import { getSyncProvider } from "@polkadot-api/json-rpc-provider-proxy";
var validateAddChainOptions = async (client, options) => {
  const chain = await client.addChain(options);
  try {
    chain.remove();
  } catch {
  }
};
var make = async (client, options) => {
  await validateAddChainOptions(client, options);
  const provider = getSyncProvider(async () => {
    const chain = await client.addChain(options);
    return (onMessage, onError) => {
      let connected = true;
      (async () => {
        while (connected) {
          try {
            const message = await chain.nextJsonRpcResponse();
            onMessage(message);
          } catch {
            connected = false;
            try {
              onError();
            } catch {
            }
            break;
          }
        }
      })();
      const send = (message) => {
        try {
          chain.sendJsonRpc(message);
        } catch {
        }
      };
      const disconnect = () => {
        connected = false;
        try {
          chain.remove();
        } catch {
        }
      };
      return {
        send,
        disconnect
      };
    };
  });
  return provider;
};

// src/background/smoldot-provider.ts
var smoldotProvider = async ({
  smoldotClient,
  ...options
}) => {
  const provider = await make(
    smoldotClient,
    "addChainOptions" in options ? options.addChainOptions : {
      chainSpec: options.chainSpec,
      disableJsonRpc: false,
      potentialRelayChains: options.relayChainSpec ? [
        {
          chainSpec: options.relayChainSpec,
          disableJsonRpc: true,
          databaseContent: options.relayChainDatabaseContent
        }
      ] : [],
      databaseContent: options.databaseContent
    }
  );
  return provider;
};

// src/background/createBackgroundRpc.ts
var handlers = {
  //#region content-script RPCs
  keepAlive() {
  },
  async getChain([chainSpec, relayChainGenesisHash], ctx) {
    if (!ctx)
      throw new Error("no context");
    const tabId = ctx.port.sender?.tab?.id;
    if (!tabId)
      throw new Error("Undefined tabId");
    const chains = await getChains();
    if (relayChainGenesisHash && !chains[relayChainGenesisHash])
      throw new Error(`Unknown relayChainGenesisHash ${relayChainGenesisHash}`);
    const { genesisHash, name } = await ctx.getChainData({
      chainSpec,
      relayChainGenesisHash
    });
    if (chains[genesisHash])
      return chains[genesisHash];
    const chain = {
      genesisHash,
      name,
      chainSpec,
      relayChainGenesisHash
    };
    await ctx.addChainByUserListener?.(chain, tabId);
    return chain;
  },
  getChains() {
    return getChains();
  },
  //#endregion
  //#region ExtensionPage RPCs
  deleteChain([genesisHash], ctx) {
    if (!ctx)
      throw new Error("no context");
    return ctx.lightClientPageHelper.deleteChain(genesisHash);
  },
  persistChain([chainSpec, relayChainGenesisHash], ctx) {
    if (!ctx)
      throw new Error("no context");
    return ctx.lightClientPageHelper.persistChain(
      chainSpec,
      relayChainGenesisHash
    );
  },
  async getActiveConnections([], ctx) {
    if (!ctx)
      throw new Error("no context");
    return (await ctx.lightClientPageHelper.getActiveConnections()).map(
      ({ tabId, chain: { provider, ...chain } }) => ({
        tabId,
        chain
      })
    );
  },
  disconnect([tabId, genesisHash], ctx) {
    if (!ctx)
      throw new Error("no context");
    return ctx.lightClientPageHelper.disconnect(tabId, genesisHash);
  },
  setBootNodes([genesisHash, bootNodes], ctx) {
    if (!ctx)
      throw new Error("no context");
    return ctx.lightClientPageHelper.setBootNodes(genesisHash, bootNodes);
  }
  //#endregion
};
var ALLOWED_WEB_METHODS = ["getChain", "getChains"];
var ALLOWED_CONTENT_SCRIPT_METHODS = [
  "getChain",
  "getChains",
  "keepAlive"
];
var allowedMethodsMiddleware = async (next, request, context) => {
  if (!context)
    throw new Error("no ctx");
  const { port } = context;
  if (!(port.name === PORT.EXTENSION_PAGE || port.name === PORT.CONTENT_SCRIPT && ALLOWED_CONTENT_SCRIPT_METHODS.includes(request.method) || port.name === PORT.WEB_PAGE && ALLOWED_WEB_METHODS.includes(request.method)))
    throw new RpcError("Method not found", -32601);
  return next(request, context);
};
var createBackgroundRpc = (sendMessage) => createRpc(sendMessage, handlers, [allowedMethodsMiddleware]).withClient();

// src/background/background-helper.ts
var isRegistered = false;
var register = ({
  smoldotClient,
  getWellKnownChainSpecs
}) => {
  if (isRegistered)
    throw new Error("helper already registered");
  isRegistered = true;
  supervise(smoldotClient, { onError: console.error });
  const wellKnownChainSpecsPromise = getWellKnownChainSpecs().then(
    async (chainSpecs) => chainSpecs instanceof Array ? Object.fromEntries(
      await Promise.all(
        chainSpecs.map(async (chainSpec) => {
          const { genesisHash } = await getChainData({
            smoldotClient,
            chainSpec
          });
          return [genesisHash, chainSpec];
        })
      )
    ) : chainSpecs
  );
  const initialized = wellKnownChainSpecsPromise.then(
    (wellKnownChainSpecs) => Promise.all(
      Object.values(wellKnownChainSpecs).map(
        (chainSpec) => lightClientPageHelper.persistChain(chainSpec)
      )
    )
  ).catch(
    (error) => console.error("Error persisting well-known chainspecs", error)
  );
  getChains().then((chains) => {
    Object.values(chains).forEach(
      ({ chainSpec, genesisHash, relayChainGenesisHash }) => followChain({
        smoldotClient,
        chainSpec,
        genesisHash,
        relayChainGenesisHash
      })
    );
  });
  onChainsChanged((chains) => {
    for (const [genesisHash] of followedChains) {
      if (chains[genesisHash])
        continue;
      unfollowChain(genesisHash);
    }
    Object.values(chains).forEach(
      ({ genesisHash, chainSpec, relayChainGenesisHash }) => {
        if (followedChains.has(genesisHash))
          return;
        followChain({
          smoldotClient,
          chainSpec,
          genesisHash,
          relayChainGenesisHash
        });
      }
    );
  });
  chrome.alarms.onAlarm.addListener(async (alarm) => {
    if (alarm.name !== ALARM.DATABASE_UPDATE)
      return;
    Object.values(await getChains()).forEach(
      async ({ genesisHash, chainSpec, relayChainGenesisHash }) => {
        try {
          const finalizedDatabase = await getFinalizedDatabase({
            smoldotClient,
            chainSpec,
            databaseContent: await get({
              type: "databaseContent",
              genesisHash
            }),
            relayChainGenesisHash
          });
          await set(
            { type: "databaseContent", genesisHash },
            finalizedDatabase
          );
        } catch (error) {
          console.error("Error updating DB", error);
        }
      }
    );
  });
  chrome.runtime.onInstalled.addListener(({ reason }) => {
    if (reason !== chrome.runtime.OnInstalledReason.INSTALL && reason !== chrome.runtime.OnInstalledReason.UPDATE)
      return;
    chrome.alarms.create(ALARM.DATABASE_UPDATE, {
      periodInMinutes: 2
    });
  });
  const lightClientPageHelper = {
    async deleteChain(genesisHash) {
      if ((await wellKnownChainSpecsPromise)[genesisHash])
        throw new Error("Cannot delete well-known-chain");
      await Promise.all([
        remove([
          { type: "chain", genesisHash },
          { type: "bootNodes", genesisHash },
          { type: "databaseContent", genesisHash }
        ]),
        Object.keys(activeChains).map(
          (tabId) => this.disconnect(+tabId, genesisHash)
        )
      ]);
      for (const {
        genesisHash: parachainGenesisHash,
        relayChainGenesisHash
      } of Object.values(await getChains())) {
        if (relayChainGenesisHash !== genesisHash)
          continue;
        await this.deleteChain(parachainGenesisHash);
      }
    },
    async persistChain(chainSpec, relayChainGenesisHash) {
      const chainData = await getChainData({
        smoldotClient,
        chainSpec,
        relayChainGenesisHash
      });
      if (await get({ type: "chain", genesisHash: chainData.genesisHash }))
        return;
      const chainSpecJson = JSON.parse(chainSpec);
      const bootNodes = chainSpecJson.bootNodes;
      let minimalChainSpec = "";
      delete chainSpecJson.bootNodes;
      delete chainSpecJson.protocolId;
      delete chainSpecJson.telemetryEndpoints;
      if (!chainSpecJson.genesis.stateRootHash) {
        chainSpecJson.genesis.stateRootHash = await getGenesisStateRoot({
          smoldotClient,
          chainSpec,
          relayChainGenesisHash
        });
      }
      minimalChainSpec = JSON.stringify(chainSpecJson);
      await Promise.all([
        set(
          { type: "chain", genesisHash: chainData.genesisHash },
          {
            ...chainData,
            chainSpec: minimalChainSpec,
            relayChainGenesisHash
          }
        ),
        set(
          { type: "bootNodes", genesisHash: chainData.genesisHash },
          bootNodes
        )
      ]);
    },
    async getChains() {
      const chains = await getChains();
      return Promise.all(
        Object.entries(chains).map(async ([genesisHash, chain]) => ({
          ...chain,
          bootNodes: await get({ type: "bootNodes", genesisHash }) ?? JSON.parse(chain.chainSpec).bootNodes,
          provider: await createSmoldotProvider({
            smoldotClient,
            chainSpec: chain.chainSpec,
            genesisHash: chain.genesisHash,
            relayChainGenesisHash: chain.relayChainGenesisHash
          })
        }))
      );
    },
    async getActiveConnections() {
      return Object.entries(activeChains).reduce(
        (acc, [tabIdStr, tabChains]) => {
          const tabId = parseInt(tabIdStr);
          Object.values(tabChains).forEach(
            ({
              genesisHash,
              name,
              ss58Format,
              bootNodes,
              chainSpec,
              relayChainGenesisHash
            }) => acc.push({
              tabId,
              chain: {
                genesisHash,
                chainSpec,
                relayChainGenesisHash,
                name,
                ss58Format,
                bootNodes,
                provider: createSmoldotSyncProvider({
                  smoldotClient,
                  chainSpec,
                  genesisHash,
                  relayChainGenesisHash
                })
              }
            })
          );
          return acc;
        },
        []
      );
    },
    async disconnect(tabId, genesisHash) {
      Object.entries(activeChains[tabId] ?? {}).filter(
        ([_, { genesisHash: activeGenesisHash }]) => activeGenesisHash === genesisHash
      ).forEach(([chainId]) => {
        removeChain(tabId, chainId);
        chrome.tabs.sendMessage(tabId, {
          origin: "substrate-connect-extension",
          type: "error",
          chainId,
          errorMessage: "Disconnected"
        });
      });
    },
    setBootNodes(genesisHash, bootNodes) {
      return set({ type: "bootNodes", genesisHash }, bootNodes);
    }
  };
  const activeChains = {};
  const helperPortNames = [
    PORT.CONTENT_SCRIPT,
    PORT.EXTENSION_PAGE,
    PORT.WEB_PAGE
  ];
  chrome.runtime.onConnect.addListener((port) => {
    if (!helperPortNames.includes(port.name))
      return;
    const tabId = port.sender?.tab?.id ?? chrome.tabs.TAB_ID_NONE;
    const postMessage = (message) => port.postMessage(message);
    const rpc = createBackgroundRpc(postMessage);
    const unsubscribeOnChainsChanged = onChainsChanged(
      (chains) => rpc.notify("onAddChains", [chains])
    );
    let isPortDisconnected = false;
    port.onDisconnect.addListener(() => {
      isPortDisconnected = true;
      unsubscribeOnChainsChanged();
      if (!activeChains[tabId])
        return;
      for (const [chainId, { chain }] of Object.entries(activeChains[tabId])) {
        try {
          chain.remove();
        } catch (error) {
          console.error("error removing chain", error);
        }
        delete activeChains[tabId][chainId];
      }
      delete activeChains[tabId];
    });
    const pendingAddChains = {};
    port.onMessage.addListener(async (msg) => {
      await initialized;
      if (isRpcMessage(msg))
        return rpc.handle(msg, {
          port,
          lightClientPageHelper,
          addChainByUserListener,
          getChainData: ({ chainSpec, relayChainGenesisHash }) => getChainData({
            smoldotClient,
            chainSpec,
            relayChainGenesisHash
          })
        });
      else if (isSubstrateConnectToExtensionMessage(msg))
        switch (msg.type) {
          case "add-well-known-chain":
          case "add-chain": {
            activeChains[tabId] ?? (activeChains[tabId] = {});
            try {
              if (activeChains[tabId][msg.chainId] || pendingAddChains[msg.chainId])
                throw new Error("Requested chainId already in use");
              pendingAddChains[msg.chainId] = true;
              const chains = await getChains();
              let addChainOptions;
              if (msg.type === "add-well-known-chain") {
                const chain = Object.values(chains).find(
                  (chain2) => chain2.genesisHash === msg.chainName
                ) ?? Object.values(chains).find(
                  (chain2) => chain2.name === msg.chainName
                );
                if (!chain)
                  throw new Error("Unknown well-known chain");
                addChainOptions = {
                  chainSpec: chain.chainSpec,
                  disableJsonRpc: false,
                  potentialRelayChains: chain.relayChainGenesisHash ? [
                    {
                      chainSpec: chains[chain.relayChainGenesisHash].chainSpec,
                      disableJsonRpc: true,
                      databaseContent: await get({
                        type: "databaseContent",
                        genesisHash: chain.relayChainGenesisHash
                      })
                    }
                  ] : [],
                  databaseContent: await get({
                    type: "databaseContent",
                    genesisHash: chain.genesisHash
                  })
                };
              } else {
                const relayChainGenesisHashOrChainId = msg.potentialRelayChainIds[0];
                addChainOptions = {
                  chainSpec: msg.chainSpec,
                  disableJsonRpc: false,
                  potentialRelayChains: chains[relayChainGenesisHashOrChainId] ? [
                    {
                      chainSpec: chains[relayChainGenesisHashOrChainId].chainSpec,
                      disableJsonRpc: true,
                      databaseContent: await get({
                        type: "databaseContent",
                        genesisHash: relayChainGenesisHashOrChainId
                      })
                    }
                  ] : msg.potentialRelayChainIds.filter((chainId) => activeChains[tabId][chainId]).map((chainId) => ({
                    chainSpec: activeChains[tabId][chainId].chainSpec,
                    disableJsonRpc: true
                  }))
                };
              }
              const [smoldotChain, { genesisHash, name, ss58Format }] = await Promise.all([
                smoldotClient.addChain(addChainOptions),
                getChainData({ smoldotClient, addChainOptions })
              ]);
              (async () => {
                while (true) {
                  let jsonRpcMessage;
                  try {
                    jsonRpcMessage = await smoldotChain.nextJsonRpcResponse();
                  } catch (_) {
                    break;
                  }
                  if (isPortDisconnected)
                    break;
                  try {
                    postMessage({
                      origin: "substrate-connect-extension",
                      type: "rpc",
                      chainId: msg.chainId,
                      jsonRpcMessage
                    });
                  } catch (error) {
                    console.error(
                      "JSON-RPC callback has thrown an exception:",
                      error
                    );
                  }
                }
              })();
              if (!pendingAddChains[msg.chainId]) {
                smoldotChain.remove();
                return;
              }
              delete pendingAddChains[msg.chainId];
              let relayChainGenesisHash = void 0;
              if (msg.type === "add-chain") {
                const relayChainGenesisHashOrChainId = msg.potentialRelayChainIds[0];
                relayChainGenesisHash = chains[relayChainGenesisHashOrChainId] ? chains[relayChainGenesisHashOrChainId].genesisHash : msg.potentialRelayChainIds.filter((chainId) => activeChains[tabId][chainId]).map(
                  (chainId) => activeChains[tabId][chainId].genesisHash
                )[0];
              }
              activeChains[tabId][msg.chainId] = {
                chain: smoldotChain,
                genesisHash,
                relayChainGenesisHash,
                chainSpec: addChainOptions.chainSpec,
                name,
                ss58Format,
                bootNodes: JSON.parse(addChainOptions.chainSpec)?.bootNodes ?? []
              };
              postMessage({
                origin: "substrate-connect-extension",
                type: "chain-ready",
                chainId: msg.chainId
              });
            } catch (error) {
              delete pendingAddChains[msg.chainId];
              postMessage({
                origin: "substrate-connect-extension",
                type: "error",
                chainId: msg.chainId,
                errorMessage: error instanceof Error ? error.toString() : "Unknown error when adding chain"
              });
            }
            break;
          }
          case "remove-chain": {
            delete pendingAddChains[msg.chainId];
            removeChain(tabId, msg.chainId);
            break;
          }
          case "rpc": {
            const chain = activeChains?.[tabId]?.[msg.chainId]?.chain;
            if (!chain)
              return;
            try {
              chain.sendJsonRpc(msg.jsonRpcMessage);
            } catch (error) {
              removeChain(tabId, msg.chainId);
              postMessage({
                origin: "substrate-connect-extension",
                type: "error",
                chainId: msg.chainId,
                errorMessage: error instanceof Error ? error.toString() : "Unknown error when sending RPC message"
              });
            }
            break;
          }
          default: {
            const unrecognizedMsg = msg;
            console.warn("Unrecognized message", unrecognizedMsg);
            break;
          }
        }
      else
        console.warn("Unrecognized message", msg);
    });
  });
  let addChainByUserListener = void 0;
  const addOnAddChainByUserListener = async (onAddChainByUser) => {
    if (addChainByUserListener)
      throw new Error("addChainByUserCallback is already set");
    addChainByUserListener = onAddChainByUser;
  };
  const removeChain = (tabId, chainId) => {
    const chain = activeChains?.[tabId]?.[chainId]?.chain;
    delete activeChains?.[tabId]?.[chainId];
    try {
      chain?.remove();
    } catch (error) {
      console.error("error removing chain", error);
    }
  };
  return { lightClientPageHelper, addOnAddChainByUserListener };
};
var withClient = (fn) => async (options) => {
  const client = createClient(
    await smoldotProvider(
      "addChainOptions" in options ? options : {
        smoldotClient: options.smoldotClient,
        chainSpec: options.chainSpec,
        databaseContent: options.databaseContent,
        relayChainSpec: options.relayChainGenesisHash ? (await getChains())[options.relayChainGenesisHash].chainSpec : void 0,
        relayChainDatabaseContent: options.relayChainGenesisHash ? await get({
          type: "databaseContent",
          genesisHash: options.relayChainGenesisHash
        }) : void 0
      }
    )
  );
  try {
    return await fn(client);
  } finally {
    client.destroy();
  }
};
var withClientChainHead$ = (fn) => withClient(async (client) => {
  const chainHead = getObservableClient(client).chainHead$();
  try {
    return await fn(chainHead, client);
  } finally {
    chainHead.unfollow();
  }
});
var getChainData = withClient(async (client) => {
  const [genesisHash, name, { ss58Format }] = await Promise.all(
    [
      "chainSpec_v1_genesisHash",
      "chainSpec_v1_chainName",
      "chainSpec_v1_properties"
    ].map((method) => substrateClientRequest(client, method))
  );
  return {
    genesisHash,
    name,
    ss58Format
  };
});
var getGenesisStateRoot = withClientChainHead$(
  async ({ runtime$ }, client) => {
    await firstValueFrom(runtime$);
    const genesisHash = await substrateClientRequest(
      client,
      "chainSpec_v1_genesisHash"
    );
    const { stateRoot } = await substrateClientRequest(client, "chain_getHeader", [genesisHash]);
    return stateRoot;
  }
);
var getFinalizedDatabase = withClientChainHead$(
  async ({ runtime$ }, client) => {
    await firstValueFrom(runtime$);
    const finalizedDatabase = await substrateClientRequest(
      client,
      "chainHead_unstable_finalizedDatabase",
      await chrome.permissions.contains({
        permissions: ["unlimitedStorage"]
      }) ? [] : (
        // 1mb will strip the runtime code
        // See https://github.com/smol-dot/smoldot/blob/0a9e9cd802169bc07dd681e55278fd67c6f8f9bc/light-base/src/database.rs#L134-L140
        [1024 * 1024]
      )
    );
    return finalizedDatabase;
  }
);
var substrateClientRequest = (client, method, params = []) => new Promise((resolve, reject) => {
  try {
    client._request(method, params, {
      onSuccess: resolve,
      onError: reject
    });
  } catch (error) {
    reject(error);
  }
});
var followedChains = /* @__PURE__ */ new Map();
var followChain = ({
  smoldotClient,
  chainSpec,
  genesisHash,
  relayChainGenesisHash
}) => {
  const subscription = new Observable((observer) => {
    observer.next(false);
    const client = getObservableClient(
      createClient(
        createSmoldotSyncProvider({
          smoldotClient,
          chainSpec,
          genesisHash,
          relayChainGenesisHash
        })
      )
    );
    let unfollow;
    const finalizedSubscription = defer(() => {
      const chainHead = client.chainHead$();
      unfollow = chainHead.unfollow;
      return chainHead.finalized$;
    }).pipe(
      catchError(() => {
        observer.next(false);
        return EMPTY;
      }),
      repeat({ delay: 1 })
    ).subscribe({
      next() {
        observer.next(true);
      },
      error: observer.error,
      complete: observer.complete
    });
    return () => {
      finalizedSubscription.unsubscribe();
      unfollow();
      client.destroy();
    };
  }).subscribe();
  followedChains.set(genesisHash, () => {
    followedChains.delete(genesisHash);
    subscription.unsubscribe();
  });
};
var unfollowChain = (genesisHash) => {
  followedChains.get(genesisHash)?.();
};
var createSmoldotProvider = async ({
  smoldotClient,
  chainSpec,
  genesisHash,
  relayChainGenesisHash
}) => smoldotProvider({
  smoldotClient,
  chainSpec,
  relayChainSpec: relayChainGenesisHash ? (await getChains())[relayChainGenesisHash].chainSpec : void 0,
  databaseContent: await get({
    type: "databaseContent",
    genesisHash
  }),
  relayChainDatabaseContent: relayChainGenesisHash ? await get({
    type: "databaseContent",
    genesisHash: relayChainGenesisHash
  }) : void 0
});
var createSmoldotSyncProvider = (options) => getSyncProvider2(() => createSmoldotProvider(options));
export {
  register
};
//# sourceMappingURL=background-helper.mjs.map

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


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