From 746fc2d823b78eec424a54bd1ca6353cbcaf910f Mon Sep 17 00:00:00 2001 From: l0r1s Date: Tue, 11 Apr 2023 19:27:25 +0300 Subject: [PATCH] feat: refactored convert command logic to fix issues --- .../packages/cli/src/actions/convert.ts | 203 +++++++++++------- javascript/packages/cli/src/types.ts | 94 +++++--- 2 files changed, 187 insertions(+), 110 deletions(-) diff --git a/javascript/packages/cli/src/actions/convert.ts b/javascript/packages/cli/src/actions/convert.ts index acaef837b..a62d80c7b 100644 --- a/javascript/packages/cli/src/actions/convert.ts +++ b/javascript/packages/cli/src/actions/convert.ts @@ -1,24 +1,22 @@ -import type { - NodeConfig, - ParachainConfig, - PolkadotLaunchConfig, -} from "@zombienet/orchestrator"; -import { decorators, getFilePathNameExt } from "@zombienet/utils"; -import fs from "fs"; +import type { PolkadotLaunchConfig as ZombienetConfig } from "@zombienet/orchestrator"; +import { decorators } from "@zombienet/utils"; +import fs from "fs/promises"; import path from "path"; -import { PL_ConfigType, PL_NodesConfig } from "src/types"; +import { PolkadotLaunch } from "src/types"; import { DEFAULT_BALANCE } from "../constants"; -export async function convert(param: string) { - try { - const filePath = param; +type ArrayElement = + ArrayType extends readonly (infer ElementType)[] ? ElementType : never; +export async function convert(filePath: string) { + try { if (!filePath) { throw Error("Path of configuration file was not provided"); } - // Read through the JSON and write to stream sample - await convertInput(filePath); + const { baseName, config } = await readPolkadotLaunchConfigFile(filePath); + const convertedConfig = await convertConfig(config); + await persistConfig(convertedConfig, baseName); } catch (err) { console.log( `\n ${decorators.red("Error: ")} \t ${decorators.bright(err)}\n`, @@ -26,92 +24,135 @@ export async function convert(param: string) { } } -// Convert functions -// Read the input file -async function readInputFile( - ext: string, - fPath: string, -): Promise { - if (ext === "json" || ext === "js") { - return ext === "json" - ? JSON.parse(fs.readFileSync(`${fPath}`, "utf8")) - : await import(path.resolve(fPath)); +async function readPolkadotLaunchConfigFile(filePath: string): Promise<{ + baseName: string; + config: PolkadotLaunch.LaunchConfig; +}> { + const extension = path.extname(filePath); + const baseName = path.basename(filePath, extension); + let config; + + if (extension === ".json") { + config = JSON.parse(await fs.readFile(filePath, "utf-8")); + } else if (extension === ".js") { + config = (await import(path.resolve(filePath))).config; + } else { + throw new Error("No valid extension was found."); } - throw Error("No valid extension was found."); + return { baseName, config }; } -async function convertInput(filePath: string) { - const { fullPath, fileName, extension } = getFilePathNameExt(filePath); - - const convertedJson = await readInputFile(extension, filePath); +function convertConfig(config: PolkadotLaunch.LaunchConfig): ZombienetConfig { + const relaychain = convertRelaychain(config.relaychain); + const parachains = convertParachains( + config.simpleParachains, + config.parachains, + ); + const hrmpChannels = convertHrmpChannels(config.hrmpChannels); - const { + return { relaychain, - parachains = [], - simpleParachains = [], - hrmpChannels = [], - types, - } = convertedJson; + parachains, + hrmp_channels: hrmpChannels, + types: config.types, + }; +} - const nodes: NodeConfig[] = []; - let paras: ParachainConfig[] = []; +function convertRelaychain( + relaychain: PolkadotLaunch.LaunchConfig["relaychain"], +): ZombienetConfig["relaychain"] { + const { chain, genesis, nodes = [], bin } = relaychain; - const DEFAULT_NODE_VALUES = { + const convertedNodes = nodes.map((node) => ({ + name: node.name, + args: node.flags, + ws_port: node.wsPort, + rpc_port: node.rpcPort, + p2p_port: node.port, + balance: DEFAULT_BALANCE, validator: true, invulnerable: true, - balance: DEFAULT_BALANCE, + })); + + return { + chain, + default_command: bin, + genesis, + nodes: convertedNodes, }; +} - paras = paras.concat( - parachains.map(({ id, nodes }) => ({ - id, - collators: ((nodes as PL_NodesConfig[]) || []).map(({ name }) => ({ - name, - command: "adder-collator", - ...DEFAULT_NODE_VALUES, - })), - })), +function convertParachains( + simpleParachains: PolkadotLaunch.LaunchConfig["simpleParachains"] = [], + parachains: PolkadotLaunch.LaunchConfig["parachains"] = [], +): ZombienetConfig["parachains"] { + const convertedSimpleParachains = simpleParachains.map( + convertSimpleParachain, ); + const convertedParachains = parachains.map(convertParachain); - paras = paras.concat( - simpleParachains.map(({ id, name }) => ({ - id, - collators: [{ name, command: "adder-collator", ...DEFAULT_NODE_VALUES }], - })), - ); + return convertedSimpleParachains.concat(convertedParachains); +} - if (relaychain?.nodes) { - relaychain.nodes.forEach((n: any) => { - nodes.push({ - name: `"${n.name}"`, - ...DEFAULT_NODE_VALUES, - }); - }); - } +function convertSimpleParachain( + simpleParachain: ArrayElement< + PolkadotLaunch.LaunchConfig["simpleParachains"] + >, +): ArrayElement { + const { id, balance, port, bin } = simpleParachain; - const jsonOutput: PolkadotLaunchConfig = { - relaychain: { - default_image: "docker.io/paritypr/polkadot-debug:master", - default_command: "polkadot", - default_args: ["-lparachain=debug"], - chain: relaychain?.chain || "", - nodes, - genesis: relaychain?.genesis, - }, - types, - hrmp_channels: hrmpChannels, - parachains: paras, + const collator = { + name: "alice", + command: bin, + p2p_port: +port, + balance: +balance || DEFAULT_BALANCE, + validator: true, + invulnerable: true, }; - fs.writeFile( - `${fullPath}/${fileName}-zombienet.json`, - JSON.stringify(jsonOutput), - (error: any) => { - if (error) throw error; - }, + return { id: +id, collators: [collator] }; +} + +function convertParachain( + parachain: ArrayElement, +): ArrayElement { + const { id = "2000", balance, chain, nodes, bin } = parachain; + + const collators = nodes.map( + ({ name = "", flags, rpcPort, wsPort, port }) => ({ + name, + command: bin, + args: flags, + ws_port: wsPort, + rpc_port: rpcPort, + p2p_port: port, + balance: +balance || DEFAULT_BALANCE, + validator: true, + invulnerable: true, + }), ); - console.log( - `Converted JSON config exists now under: ${fullPath}/${fileName}-zombienet.json`, + + return { id: +id, chain, collators }; +} + +function convertHrmpChannels( + hrmpChannels: PolkadotLaunch.LaunchConfig["hrmpChannels"], +): ZombienetConfig["hrmp_channels"] { + return hrmpChannels.map( + ({ sender, recipient, maxCapacity, maxMessageSize }) => ({ + sender, + recipient, + max_capacity: maxCapacity, + max_message_size: maxMessageSize, + }), ); } + +async function persistConfig(config: ZombienetConfig, baseName: string) { + const content = JSON.stringify(config); + const path = `${baseName}-zombienet.json`; + + await fs.writeFile(path, content); + console.log(`Converted JSON config exists now under: ${path}`); +} diff --git a/javascript/packages/cli/src/types.ts b/javascript/packages/cli/src/types.ts index 3002dc257..e8acc6a51 100644 --- a/javascript/packages/cli/src/types.ts +++ b/javascript/packages/cli/src/types.ts @@ -1,34 +1,70 @@ -import type { HrmpChannelsConfig, ObjectJSON } from "@zombienet/orchestrator"; +// eslint-disable-next-line +export namespace PolkadotLaunch { + export interface LaunchConfig { + relaychain: RelayChainConfig; + parachains: ParachainConfig[]; + simpleParachains: SimpleParachainConfig[]; + hrmpChannels: HrmpChannelsConfig[]; + types: any; + finalization: boolean; + } -// Config interfaces -export interface PL_NodesConfig { - name: string; - wsPort: number; - port: number; - flags?: [string]; -} + export interface RelayChainConfig { + bin: string; + chain: string; + nodes: { + name: string; + basePath?: string; + wsPort: number; + rpcPort?: number; + nodeKey?: string; + port: number; + flags?: string[]; + }[]; + genesis?: JSON | ObjectJSON; + } -export interface PL_RelayChainConfig { - bin?: string; - chain: string; - nodes: [PL_NodesConfig]; - genesis?: JSON | ObjectJSON; -} + export interface ParachainConfig { + bin: string; + id?: string; + balance: string; + chain?: string; + nodes: ParachainNodeConfig[]; + } -export interface PL_ParaChainConfig { - bin?: string; - name?: string; - id: number; - port?: string; - balance?: string; - nodes: [PL_NodesConfig]; -} + export interface ParachainNodeConfig { + rpcPort?: number; + wsPort: number; + port: number; + basePath?: string; + name?: string; + flags: string[]; + } + + export interface SimpleParachainConfig { + bin: string; + id: string; + port: string; + balance: string; + } + + export interface HrmpChannelsConfig { + sender: number; + recipient: number; + maxCapacity: number; + maxMessageSize: number; + } + + export interface CollatorOptions { + name?: string; + spec?: string; + flags?: string[]; + basePath?: string; + chain?: string; + onlyOneParachainNode?: boolean; + } -export interface PL_ConfigType { - relaychain?: PL_RelayChainConfig; - parachains?: [PL_ParaChainConfig]; - simpleParachains?: [PL_NodesConfig & { id: number }]; - hrmpChannels?: HrmpChannelsConfig[]; - types?: any; - finalization?: boolean; + export interface ObjectJSON { + [key: string]: ObjectJSON | number | string; + } }