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Выполнить команду
Для локальной разработки. Не используйте в интернете!