From 8f11c7abbeddcae4223c675c09ae73e3f172e03a Mon Sep 17 00:00:00 2001 From: Tarik Gul <47201679+TarikGul@users.noreply.github.com> Date: Thu, 2 Nov 2023 12:06:35 -0400 Subject: [PATCH] test: add foreign assets script to test network (#272) Co-authored-by: bee344 Co-authored-by: Alberto Nicolas Penayo <74352651+bee344@users.noreply.github.com> --- package.json | 1 + scripts/consts.ts | 5 + scripts/testNetworkForeignAssets.ts | 220 ++++++++++++++++++++++++++++ scripts/testNetworkSetup.ts | 63 +------- scripts/util.ts | 59 ++++++++ zombienet/medium-network.toml | 52 +------ 6 files changed, 295 insertions(+), 105 deletions(-) create mode 100644 scripts/consts.ts create mode 100644 scripts/testNetworkForeignAssets.ts create mode 100644 scripts/util.ts diff --git a/package.json b/package.json index cd4b2de0..f2c04398 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build:examples": "substrate-exec-rimraf examples/build/ && substrate-exec-tsc --project examples/tsconfig.json", "start": "node ./lib/index.js", "start:zombienet-post-script": "yarn build:scripts && node ./scripts/build/testNetworkSetup.js", + "start:zombienet-foreign-assets-script": "yarn build:scripts && node ./scripts/build/testNetworkForeignAssets.js", "lint": "substrate-dev-run-lint", "lint:fix": "substrate-dev-run-lint --fix", "test": "NODE_ENV=test substrate-exec-jest --detectOpenHandles", diff --git a/scripts/consts.ts b/scripts/consts.ts new file mode 100644 index 00000000..fac167a5 --- /dev/null +++ b/scripts/consts.ts @@ -0,0 +1,5 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +export const KUSAMA_ASSET_HUB_WS_URL = 'ws://127.0.0.1:9911'; +export const ROCOCO_ALICE_WS_URL = 'ws://127.0.0.1:9900'; +export const TRAPPIST_WS_URL = 'ws://127.0.0.1:9921'; diff --git a/scripts/testNetworkForeignAssets.ts b/scripts/testNetworkForeignAssets.ts new file mode 100644 index 00000000..d8789c64 --- /dev/null +++ b/scripts/testNetworkForeignAssets.ts @@ -0,0 +1,220 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +import { ApiPromise, WsProvider } from '@polkadot/api'; +import { Keyring } from '@polkadot/keyring'; +import { cryptoWaitReady } from '@polkadot/util-crypto'; +import chalk from 'chalk'; + +import { KUSAMA_ASSET_HUB_WS_URL, TRAPPIST_WS_URL } from './consts'; +import { awaitBlockProduction, delay, logWithDate } from './util'; + +const fAssetSetMetadataCall = (assetHubApi: ApiPromise): `0x${string}` => { + const trappistMultiLocation = { + parents: 1, + interior: { + X1: { + parachain: 1836, + }, + }, + }; + + const setMetadataTx = assetHubApi.tx.foreignAssets.setMetadata(trappistMultiLocation, 'Trappist Hop', 'Hop', 12); + + const hexCall = assetHubApi.registry + .createType('Call', { + callIndex: setMetadataTx.callIndex, + args: setMetadataTx.args, + }) + .toHex(); + + return hexCall; +}; + +const fAssetCreateCall = (assetHubApi: ApiPromise): `0x${string}` => { + const trappistMultiLocation = { + parents: 1, + interior: { + X1: { + parachain: 1836, + }, + }, + }; + + const createTx = assetHubApi.tx.foreignAssets.create( + trappistMultiLocation, + '5Eg2fnsjAAr8RGZfa8Sy5mYFPabA9ZLNGYECCKXPD6xnK6D2', // Sibling 1836 -> ParaId + '100000000000' + ); + + const hexCall = assetHubApi.registry + .createType('Call', { + callIndex: createTx.callIndex, + args: createTx.args, + }) + .toHex(); + + return hexCall; +}; + +const sudoCallWrapper = (trappistApi: ApiPromise, call: `0x${string}`) => { + // Double encode the call + const xcmDoubleEncoded = trappistApi.createType('XcmDoubleEncoded', { + encoded: call, + }); + + const xcmOriginType = trappistApi.createType('XcmOriginKind', 'Xcm'); + const xcmDestMultiLocation = { + V3: { + parents: 1, + interior: { + X1: { + parachain: 1000, + }, + }, + }, + }; + const xcmMessage = { + V3: [ + { + withdrawAsset: [ + { + id: { + concrete: { + parents: 1, + interior: { Here: '' }, + }, + }, + fun: { + fungible: 100000000000, + }, + }, + ], + }, + { + buyExecution: { + fees: { + id: { + concrete: { + parents: 1, + interior: { Here: '' }, + }, + }, + fun: { + fungible: 100000000000, + }, + }, + weightLimit: { Unlimited: '' }, + }, + }, + { + transact: { + originKind: xcmOriginType, + requireWeightAtMost: { + refTime: 8000000000, + proofSize: 65536, + }, + call: xcmDoubleEncoded, + }, + }, + { + refundSurplus: true, + }, + { + depositAsset: { + assets: { + wild: { + All: '', + }, + }, + beneficiary: { + parents: 0, + interior: { + x1: { + AccountId32: { + id: '0x7369626c2c070000000000000000000000000000000000000000000000000000', + }, + }, + }, + }, + }, + }, + ], + }; + const xcmMsg = trappistApi.tx.polkadotXcm.send(xcmDestMultiLocation, xcmMessage); + const xcmCall = trappistApi.createType('Call', { + callIndex: xcmMsg.callIndex, + args: xcmMsg.args, + }); + + return xcmCall; +}; + +const createForeignAssetViaSudo = (assetHubApi: ApiPromise, trappistApi: ApiPromise) => { + const foreignAssetCreateCall = fAssetCreateCall(assetHubApi); + return sudoCallWrapper(trappistApi, foreignAssetCreateCall); +}; + +const setMetadataForeignAssetViaSudo = (assetHubApi: ApiPromise, trappistApi: ApiPromise) => { + const setMetadataCall = fAssetSetMetadataCall(assetHubApi); + return sudoCallWrapper(trappistApi, setMetadataCall); +}; + +const main = async () => { + logWithDate(chalk.yellow('Initializing script to create foreignAssets on chain')); + await cryptoWaitReady(); + + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + const bob = keyring.addFromUri('//Bob'); + + const kusamaAssetHubApi = await ApiPromise.create({ + provider: new WsProvider(KUSAMA_ASSET_HUB_WS_URL), + noInitWarn: true, + }); + + await kusamaAssetHubApi.isReady; + logWithDate(chalk.green('Created a connection to Kusama AssetHub')); + + const trappistApi = await ApiPromise.create({ + provider: new WsProvider(TRAPPIST_WS_URL), + noInitWarn: true, + }); + + await trappistApi.isReady; + logWithDate(chalk.green('Created a connection to Trappist')); + + logWithDate(chalk.magenta('Sending funds to Trappist Sibling on Kusama AssetHub')); + + await kusamaAssetHubApi.tx.balances + .transferKeepAlive('5Eg2fnsjAAr8RGZfa8Sy5mYFPabA9ZLNGYECCKXPD6xnK6D2', 10000000000000) + .signAndSend(bob); + + const foreignAssetsCreateSudoXcmCall = createForeignAssetViaSudo(kusamaAssetHubApi, trappistApi); + + logWithDate('Sending Sudo XCM message from relay chain to execute create foreign asset call on Kusama AssetHub'); + await trappistApi.tx.sudo.sudo(foreignAssetsCreateSudoXcmCall).signAndSend(alice); + + await delay(24000); + + const foreignAssetsSetMetadataSudoXcmCall = setMetadataForeignAssetViaSudo(kusamaAssetHubApi, trappistApi); + + logWithDate('Sending Sudo XCM message from relay chain to execute setMetadata call on Kusama AssetHub'); + await trappistApi.tx.sudo.sudo(foreignAssetsSetMetadataSudoXcmCall).signAndSend(alice); + + await delay(24000); + + await kusamaAssetHubApi.disconnect().then(() => { + logWithDate(chalk.blue('Polkadot-js successfully disconnected from asset-hub')); + }); + + await trappistApi.disconnect().then(() => { + logWithDate(chalk.blue('Polkadot-js successfully disconnected from trappist')); + }); +}; + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +awaitBlockProduction(TRAPPIST_WS_URL).then(async () => { + await main() + .catch(console.error) + .finally(() => process.exit()); +}); diff --git a/scripts/testNetworkSetup.ts b/scripts/testNetworkSetup.ts index fda13850..06d49cf6 100644 --- a/scripts/testNetworkSetup.ts +++ b/scripts/testNetworkSetup.ts @@ -5,72 +5,17 @@ import '@polkadot/api-augment'; import { ApiPromise, WsProvider } from '@polkadot/api'; import { Keyring } from '@polkadot/keyring'; import { DispatchError } from '@polkadot/types/interfaces'; -import { formatDate } from '@polkadot/util'; import { cryptoWaitReady } from '@polkadot/util-crypto'; import chalk from 'chalk'; +import { KUSAMA_ASSET_HUB_WS_URL, ROCOCO_ALICE_WS_URL } from './consts'; +import { awaitBlockProduction, delay, logWithDate } from './util'; + /** * This script is intended to be run after zombienet is running. * It uses the hard coded values given in `zombienet.toml`. */ -const KUSAMA_ASSET_HUB_WS_URL = 'ws://127.0.0.1:9911'; -const ROCOCO_ALICE_WS_URL = 'ws://127.0.0.1:9900'; - -/** - * Set a delay (sleep) - * - * @param ms Milliseconds - */ -const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -/** - * Formats a string to match the output of polkadot-js logging. - * - * @param log String to be logged - * @param remove Remove lines before that were cleared by std - */ -const logWithDate = (log: string, remove?: boolean) => { - remove - ? console.log(`\r${formatDate(new Date())} ${log}`) - : console.log(`${formatDate(new Date())} ${log}`); -}; - -/** - * Will block the main script from running until there is blocks in Polkadot AssetHub being produced. - */ -const awaitBlockProduction = async () => { - logWithDate(chalk.yellow(`Initializing polkadot-js: Polling until ${KUSAMA_ASSET_HUB_WS_URL} is available`)); - const kusamaAssetHubApi = await ApiPromise.create({ - provider: new WsProvider(KUSAMA_ASSET_HUB_WS_URL), - noInitWarn: true, - }); - logWithDate(chalk.yellow('Polkadot-js is connected')); - - await kusamaAssetHubApi.isReady; - - let counter = 3; - let blocksProducing = false; - while (!blocksProducing) { - const { number } = await kusamaAssetHubApi.rpc.chain.getHeader(); - - if (number.toNumber() > 0) { - blocksProducing = true; - } - await delay(1000); - - counter += 1; - process.stdout.clearLine(0); - process.stdout.write(`\rWaiting for Block production on Kusama AssetHub${'.'.repeat((counter % 3) + 1)}`); - } - - process.stdout.clearLine(0); - logWithDate(chalk.magenta('Blocks are producing'), true); - await kusamaAssetHubApi.disconnect().then(() => { - logWithDate(chalk.blue('Polkadot-js successfully disconnected')); - }); -}; - const main = async () => { logWithDate(chalk.yellow('Initializing script to run transaction on chain')); await cryptoWaitReady(); @@ -214,7 +159,7 @@ const main = async () => { }; // eslint-disable-next-line @typescript-eslint/no-floating-promises -awaitBlockProduction().then(async () => { +awaitBlockProduction(KUSAMA_ASSET_HUB_WS_URL).then(async () => { await main() .catch(console.error) .finally(() => process.exit()); diff --git a/scripts/util.ts b/scripts/util.ts new file mode 100644 index 00000000..0d3e3ad6 --- /dev/null +++ b/scripts/util.ts @@ -0,0 +1,59 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. + +import { ApiPromise, WsProvider } from '@polkadot/api'; +import { formatDate } from '@polkadot/util'; +import chalk from 'chalk'; + +/** + * Set a delay (sleep) + * + * @param ms Milliseconds + */ +export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +/** + * Formats a string to match the output of polkadot-js logging. + * + * @param log String to be logged + * @param remove Remove lines before that were cleared by std + */ +export const logWithDate = (log: string, remove?: boolean) => { + remove + ? console.log(`\r${formatDate(new Date())} ${log}`) + : console.log(`${formatDate(new Date())} ${log}`); +}; + +/** + * Will block the main script from running until there is blocks in Polkadot AssetHub being produced. + */ +export const awaitBlockProduction = async (wsUrl: string) => { + logWithDate(chalk.yellow(`Initializing polkadot-js: Polling until ${wsUrl} is available`)); + const api = await ApiPromise.create({ + provider: new WsProvider(wsUrl), + noInitWarn: true, + }); + logWithDate(chalk.yellow('Polkadot-js is connected')); + + await api.isReady; + + let counter = 3; + let blocksProducing = false; + while (!blocksProducing) { + const { number } = await api.rpc.chain.getHeader(); + + if (number.toNumber() > 0) { + blocksProducing = true; + } + await delay(1000); + + counter += 1; + process.stdout.clearLine(0); + process.stdout.write(`\rWaiting for Block production on Kusama AssetHub${'.'.repeat((counter % 3) + 1)}`); + } + + process.stdout.clearLine(0); + logWithDate(chalk.magenta('Blocks are producing'), true); + await api.disconnect().then(() => { + logWithDate(chalk.blue('Polkadot-js successfully disconnected')); + }); +}; diff --git a/zombienet/medium-network.toml b/zombienet/medium-network.toml index 0036ca1f..fe7552d2 100644 --- a/zombienet/medium-network.toml +++ b/zombienet/medium-network.toml @@ -43,39 +43,23 @@ cumulus_based = true args = ["--log=xcm=trace,pallet-assets=trace"] [[parachains]] -id = 2000 +id = 1836 add_to_genesis = true cumulus_based = true +chain = "trappist-local" [[parachains.collators]] name = "trappist-collator01" - command = "./bin/trappist-collator" + command = "./bin/trappist-node" ws_port = 9920 args = ["--log=xcm=trace,pallet-assets=trace"] [[parachains.collators]] name = "trappist-collator02" - command = "./bin/trappist-collator" + command = "./bin/trappist-node" ws_port = 9921 args = ["--log=xcm=trace,pallet-assets=trace"] -[[parachains]] -id = 3000 -add_to_genesis = true -cumulus_based = true - - [[parachains.collators]] - name = "stout-collator01" - command = "./bin/trappist-collator" - ws_port = 9930 - args = ["--log=xcm=trace,pallet-assets=trace"] - - [[parachains.collators]] - name = "stout-collator02" - command = "./bin/trappist-collator" - ws_port = 9931 - args = ["--log=xcm=trace,pallet-assets=trace"] - [types.Header] number = "u64" parent_hash = "Hash" @@ -83,36 +67,12 @@ post_state = "Hash" [[hrmp_channels]] sender = 1000 -recipient = 2000 -max_capacity = 8 -max_message_size = 512 - -[[hrmp_channels]] -sender = 2000 -recipient = 1000 -max_capacity = 8 -max_message_size = 512 - -[[hrmp_channels]] -sender = 1000 -recipient = 3000 +recipient = 1836 max_capacity = 8 max_message_size = 512 [[hrmp_channels]] -sender = 3000 +sender = 1836 recipient = 1000 max_capacity = 8 max_message_size = 512 - -[[hrmp_channels]] -sender = 2000 -recipient = 3000 -max_capacity = 8 -max_message_size = 512 - -[[hrmp_channels]] -sender = 3000 -recipient = 2000 -max_capacity = 8 -max_message_size = 512