Skip to content

Commit

Permalink
Merge pull request PolkaGate#696 from AMIRKHANEF/client
Browse files Browse the repository at this point in the history
Add Light-client
  • Loading branch information
Nick-1979 authored Sep 16, 2023
2 parents 59fbb3c + 66201ce commit 263b588
Show file tree
Hide file tree
Showing 12 changed files with 22,954 additions and 44 deletions.
22,793 changes: 22,793 additions & 0 deletions del-yarn.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
"@mui/material": "^5.8.3",
"@polkadot/api": "^10.9.1",
"@polkadot/apps-config": "^0.132.1",
"@polkadot/rpc-provider": "^10.9.1",
"@substrate/connect": "^0.7.32",
"@vaadin/icons": "^23.2.3",
"babel-plugin-transform-import-meta": "^2.1.1",
"better-react-spinkit": "^2.0.4",
Expand Down
72 changes: 46 additions & 26 deletions packages/extension-polkagate/src/hooks/useApi.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Copyright 2019-2023 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { useContext, useEffect, useState } from 'react';
import { useCallback, useContext, useEffect, useState } from 'react';

import { ApiPromise, WsProvider } from '@polkadot/api';
import { AccountId } from '@polkadot/types/interfaces/runtime';

import { APIContext } from '../components';
import LCConnector from '../util/api/lightClient-connect';
import { useChain, useEndpoint } from '.';

export default function useApi(address: AccountId | string | undefined, stateApi?: ApiPromise): ApiPromise | undefined {
Expand All @@ -16,8 +17,29 @@ export default function useApi(address: AccountId | string | undefined, stateApi

const [api, setApi] = useState<ApiPromise | undefined>(stateApi);

const handleNewApi = useCallback((api: ApiPromise, endpoint: string) => {
setApi(api);
const genesisHash = String(api.genesisHash.toHex());
const toSaveApi = apisContext.apis[genesisHash] ?? [];

const indexToDelete = toSaveApi.findIndex((sApi) => sApi.endpoint === endpoint);

if (indexToDelete !== -1) {
toSaveApi.splice(indexToDelete, 1);
}

toSaveApi.push({
api,
endpoint,
isRequested: false
});

apisContext.apis[genesisHash] = toSaveApi;
apisContext.setIt(apisContext.apis);
}, [apisContext]);

useEffect(() => {
if (!chain?.genesisHash || (api && api.isConnected && api._options.provider.endpoint === endpoint)) {
if (!chain?.genesisHash || (api && api.isConnected && api._options.provider?.endpoint === endpoint)) {
return;
}

Expand All @@ -36,43 +58,41 @@ export default function useApi(address: AccountId | string | undefined, stateApi
return;
}

// console.log('Initializing API connection...');
if (!endpoint?.startsWith('wss') && !endpoint?.startsWith('light')) {
console.log('📌 📌 Unsupported endpoint detected 📌 📌 ', endpoint);

const wsProvider = new WsProvider(endpoint);

ApiPromise.create({ provider: wsProvider })
.then((newApi) => {
console.log('API connection established successfully.');
setApi(newApi);

const toSaveApi = apisContext.apis[String(newApi.genesisHash.toHex())] ?? [];

const indexToDelete = toSaveApi.findIndex((sApi) => sApi.endpoint === endpoint);
return;
}

if (indexToDelete !== -1) {
toSaveApi.splice(indexToDelete, 1);
}
if (endpoint?.startsWith('wss')) {
const wsProvider = new WsProvider(endpoint);

toSaveApi.push({
api: newApi,
endpoint,
isRequested: false
ApiPromise.create({ provider: wsProvider })
.then((newApi) => {
handleNewApi(newApi, endpoint);
console.log('API connection established successfully.');
})
.catch((error) => {
console.error('API connection failed:', error);
});
}

apisContext.apis[String(newApi.genesisHash.toHex())] = toSaveApi;
apisContext.setIt(apisContext.apis);
})
.catch((error) => {
console.error('API connection failed:', error);
if (endpoint?.startsWith('light')) {
LCConnector(endpoint).then((LCapi) => {
handleNewApi(LCapi, endpoint);
console.log('🖌️ light client connected', String(LCapi.genesisHash.toHex()));
}).catch((err) => {
console.error('📌 light client failed:', err);
});
}

const toSaveApi = apisContext.apis[chain.genesisHash] ?? [];

toSaveApi.push({ endpoint, isRequested: true });

apisContext.apis[chain.genesisHash] = toSaveApi;
apisContext.setIt(apisContext.apis);
}, [apisContext, endpoint, stateApi, chain, api?.isConnected, api]);
}, [apisContext, endpoint, stateApi, chain, api?.isConnected, api, handleNewApi]);

useEffect(() => {
const pollingInterval = setInterval(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ export default function useConvictionOptions(address: string | AccountId | undef
return;
}

/** load pool from storage */
/** load Convictions from storage */
chrome.storage.local.get('Convictions', (res) => {
console.log('ConvictionOptions in local storage:', res);
// console.log('ConvictionOptions in local storage:', res);

if (res?.Convictions?.[genesisHash]) {
setSavedConvictionOptions(res.Convictions[genesisHash]);
Expand Down
12 changes: 10 additions & 2 deletions packages/extension-polkagate/src/hooks/useEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import { DropdownOption } from '../util/types';
import { sanitizeChainName } from '../util/utils';
import { useGenesisHashOptions, useTranslation } from './';


const supportedLC = ['Polkadot', 'Kusama', 'Westend'];

export function useEndpoints(genesisHash: string | null | undefined): DropdownOption[] {
const { t } = useTranslation();
const genesisOptions = useGenesisHashOptions();
Expand All @@ -38,8 +41,13 @@ export function useEndpoints(genesisHash: string | null | undefined): DropdownOp
(String(e.info)?.toLowerCase() === chainName?.toLowerCase() ||
String(e.text)?.toLowerCase()?.includes(chainName?.toLowerCase()))
);

return endpoints?.filter((e) => String(e.value).startsWith('ws')).map((e) => ({ text: e.textBy, value: e.value }));

return chainName
? supportedLC.includes(chainName)
? endpoints?.map((endpoint) => ({ text: endpoint.textBy, value: endpoint.value as string }))
: endpoints?.filter((e) => String(e.value).startsWith('wss')).map((e) => ({ text: e.textBy, value: e.value as string }))
: undefined;
// return endpoints?.filter((e) => String(e.value).startsWith('wss')).map((e) => ({ text: e.textBy, value: e.value }));
}, [allEndpoints, genesisHash, genesisOptions]);

return endpoints ?? [];
Expand Down
2 changes: 1 addition & 1 deletion packages/extension-polkagate/src/i18n/Backend.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017-2022 @polkadot/react-components authors & contributors
// Copyright 2019-2023 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import languageCache from './cache';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ function FullScreenRemoteNode({ address }: Props): React.ReactElement {
const calculateAndSetDelay = useCallback(() => {
endpointUrl && CalculateNodeDelay(endpointUrl)
.then((response) => {
if (!response) {
return;
}

setFetchedApiAndDelay({ fetchedApi: response.api, fetchedDelay: response.delay });
setEndpointsDelay((prevEndpoints) => {
return prevEndpoints?.map((endpoint) => {
Expand All @@ -115,7 +119,7 @@ function FullScreenRemoteNode({ address }: Props): React.ReactElement {
});
});

response.api.disconnect().catch(console.error);
response.api && response.api.disconnect().catch(console.error);
})
.catch(console.error);
}, [endpointUrl]);
Expand Down Expand Up @@ -195,7 +199,9 @@ function FullScreenRemoteNode({ address }: Props): React.ReactElement {
<Typography fontSize='16px' fontWeight={selectedEndpoint ? 500 : 400} pr='10px'>
{endpoint.name}
</Typography>
<NodeStatusAndDelay endpointDelay={endpoint.delay} isSelected={selectedEndpoint} />
{!endpoint.name.includes('light client') &&
<NodeStatusAndDelay endpointDelay={endpoint.delay} isSelected={selectedEndpoint} />
}
</Grid>
);
})}
Expand Down
53 changes: 53 additions & 0 deletions packages/extension-polkagate/src/util/api/lightClient-connect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2019-2023 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import * as Sc from '@substrate/connect';

import { ApiPromise } from '@polkadot/api';
import { ScProvider } from '@polkadot/rpc-provider/substrate-connect';

const extractChainName = (networkEndpoint: string | undefined): string | undefined => {
if (!networkEndpoint) {
return;
}

const parts = networkEndpoint.split('/');
const chainName = parts[3]; // Assuming the chain name is always at index 3
const chainNameCapitalized = chainName.charAt(0).toUpperCase() + chainName.slice(1);

return chainNameCapitalized;
};

const chainSpec = (networkName: string | undefined) => {
switch (networkName) {
case 'Polkadot':
return Sc.WellKnownChain.polkadot;
case 'Kusama':
return Sc.WellKnownChain.ksmcc3;
case 'Westend':
return Sc.WellKnownChain.westend2;
default:
return '';
}
};

export default async function LCConnector(endpoint: string): Promise<ApiPromise> {
const chainName = extractChainName(endpoint);
const currentChainSpec = chainSpec(chainName);

try {
console.log('connecting through light client, endpoint:', endpoint);

if (currentChainSpec.length) {
const provider = new ScProvider(Sc, currentChainSpec);

await provider.connect();

return await ApiPromise.create({ provider });
}

return Promise.reject(new Error(`Unsupported network: ${chainName}`));
} catch (error) {
return Promise.reject(error);
}
}
4 changes: 4 additions & 0 deletions packages/extension-polkagate/src/util/calculateNodeDelay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const fetchApiTime = async (api: ApiPromise | undefined) => {
async function CalculateNodeDelay(endpoint: string | undefined) {
const TIMEOUT = 10000;

if (!endpoint?.startsWith('wss')) {
return;
}

const wsProvider = new WsProvider(endpoint);

const api = await ApiPromise.create({ provider: wsProvider });
Expand Down
21 changes: 12 additions & 9 deletions packages/extension-polkagate/src/util/getApi.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
// Copyright 2019-2023 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0
/* eslint-disable header/header */

// import Memoize from 'memoize-one';
import memoize from 'memoizee';
/* eslint-disable header/header */

// var memoize = require("memoizee");
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getApi(endpoint: string): Promise<ApiPromise> {
const wsProvider = new WsProvider(endpoint);
import LCConnector from './api/lightClient-connect';

async function getApi(endpoint: string): Promise<ApiPromise | undefined> {
if (endpoint.startsWith('wss')) {
const wsProvider = new WsProvider(endpoint);

return await ApiPromise.create({ provider: wsProvider });
return await ApiPromise.create({ provider: wsProvider });
} else if (endpoint.startsWith('light')) {
return await LCConnector(endpoint);
} else {
throw new Error(`Invalid endpoint: ${endpoint}`);
}
}

// export default getApi;
// export default memoize(getApi);
export default getApi;
2 changes: 1 addition & 1 deletion packages/extension-polkagate/src/util/workers/getPool.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import getApi from '../getApi.ts';
import getPoolAccounts from '../getPoolAccounts';

async function getPool(endpoint, stakerAddress, id = undefined) {
console.log(`getPool is called for ${stakerAddress} id:${id}`);
console.log(`getPool is called for ${stakerAddress} id:${id} endpoint:${endpoint}`);
const api = await getApi(endpoint);
const token = api.registry.chainTokens[0];
const decimal = api.registry.chainDecimals[0];
Expand Down
23 changes: 22 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4403,7 +4403,7 @@ __metadata:
languageName: node
linkType: hard

"@polkadot/rpc-provider@npm:10.9.1, @polkadot/rpc-provider@npm:^10.7.3":
"@polkadot/rpc-provider@npm:10.9.1, @polkadot/rpc-provider@npm:^10.7.3, @polkadot/rpc-provider@npm:^10.9.1":
version: 10.9.1
resolution: "@polkadot/rpc-provider@npm:10.9.1"
dependencies:
Expand Down Expand Up @@ -5245,6 +5245,16 @@ __metadata:
languageName: node
linkType: hard

"@substrate/connect@npm:^0.7.32":
version: 0.7.32
resolution: "@substrate/connect@npm:0.7.32"
dependencies:
"@substrate/connect-extension-protocol": ^1.0.1
smoldot: 2.0.1
checksum: b2cab2519a53eb5a48efc406a93a737437ddc1b4f294226cdee6a3d8576dca25943e900fd6eeccb816031cfcfa05e4a6d365cfabc72eddacf31c428a3511a8d7
languageName: node
linkType: hard

"@substrate/smoldot-light@npm:0.7.9":
version: 0.7.9
resolution: "@substrate/smoldot-light@npm:0.7.9"
Expand Down Expand Up @@ -19138,6 +19148,8 @@ resolve@^2.0.0-next.3:
"@polkadot/api": ^10.9.1
"@polkadot/apps-config": ^0.132.1
"@polkadot/dev": latest
"@polkadot/rpc-provider": ^10.9.1
"@substrate/connect": ^0.7.32
"@testing-library/react": latest
"@types/jest": latest
"@vaadin/icons": ^23.2.3
Expand Down Expand Up @@ -19794,6 +19806,15 @@ resolve@^2.0.0-next.3:
languageName: node
linkType: hard

"smoldot@npm:2.0.1":
version: 2.0.1
resolution: "smoldot@npm:2.0.1"
dependencies:
ws: ^8.8.1
checksum: 77c1f541d039fe740157e9b81e2b13fc72dabe3ffd75644ee9958aee48d5c5458b6cc974d1e9233b1bcf3fde7af42a53a0e48452b6657405c64158a0c8168eee
languageName: node
linkType: hard

"sockjs@npm:^0.3.24":
version: 0.3.24
resolution: "sockjs@npm:0.3.24"
Expand Down

0 comments on commit 263b588

Please sign in to comment.