UNPKG

@zombienet/orchestrator

Version:

ZombieNet aim to be a testing framework for substrate based blockchains, providing a simple cli tool that allow users to spawn and test ephemeral Substrate based networks

633 lines (632 loc) 37.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.zombieWrapperPath = void 0; exports.generateNetworkSpec = generateNetworkSpec; exports.generateBootnodeSpec = generateBootnodeSpec; exports.getUniqueName = getUniqueName; exports.getFirstCollatorCommand = getFirstCollatorCommand; const fs_1 = __importDefault(require("fs")); const path_1 = __importStar(require("path")); const os_1 = __importDefault(require("os")); const utils_1 = require("@zombienet/utils"); const constants_1 = require("./constants"); const keys_1 = require("./keys"); const chain_decorators_1 = require("./chain-decorators"); const sharedTypes_1 = require("./sharedTypes"); const debug = require("debug")("zombie::config-manager"); /** * Resolves a chain spec path which can be either a local file path or HTTP/HTTPS URL * If it's a URL, downloads it to a temporary file and returns the local path * @param chainSpecPath - The path or URL to the chain spec * @param context - Context for error messages (e.g., "relaychain" or "parachain id: 1000") * @returns The resolved local file path */ function resolveChainSpecPath(chainSpecPath, context) { return __awaiter(this, void 0, void 0, function* () { if (/^https?:\/\//i.test(chainSpecPath)) { const tmpFile = path_1.default.join(os_1.default.tmpdir(), `zombienet-${context.replace(/[^a-zA-Z0-9]/g, "-")}-spec-${(0, utils_1.getSha256)(chainSpecPath)}.json`); yield (0, utils_1.downloadFile)(chainSpecPath, tmpFile); if (!fs_1.default.existsSync(tmpFile)) { console.error(utils_1.decorators.red(`Failed to download ${context} chain spec from URL: ${chainSpecPath}`)); process.exit(1); } return tmpFile; } else { const resolvedPath = (0, path_1.resolve)(process.cwd(), chainSpecPath); if (!fs_1.default.existsSync(resolvedPath)) { console.error(utils_1.decorators.red(`Chain spec provided for ${context} does not exist: ${resolvedPath}`)); process.exit(1); } return resolvedPath; } }); } // get the path of the zombie wrapper exports.zombieWrapperPath = (0, path_1.resolve)(__dirname, `../${constants_1.ZOMBIE_WRAPPER}`); const DEFAULT_ENV = [ { name: "COLORBT_SHOW_HIDDEN", value: "1" }, { name: "RUST_BACKTRACE", value: "FULL" }, ]; const isIterable = (obj) => { // checks for null and undefined if (obj == null || typeof obj == "string") { return false; } return typeof obj[Symbol.iterator] === "function"; }; const configurationFileChecks = (config) => { var _a, _b, _c, _d, _e; if (config.hrmpChannels) { throw new Error("'hrmpChannels' value the given configuration file is deprecated; Please use 'hrmp_channels' instead;"); } (0, utils_1.validateImageUrl)(((_a = config === null || config === void 0 ? void 0 : config.relaychain) === null || _a === void 0 ? void 0 : _a.default_image) || constants_1.DEFAULT_IMAGE); if (((_b = config === null || config === void 0 ? void 0 : config.relaychain) === null || _b === void 0 ? void 0 : _b.node_groups) && isIterable((_c = config === null || config === void 0 ? void 0 : config.relaychain) === null || _c === void 0 ? void 0 : _c.node_groups)) for (const nodeGroup of ((_d = config === null || config === void 0 ? void 0 : config.relaychain) === null || _d === void 0 ? void 0 : _d.node_groups) || []) { (0, utils_1.validateImageUrl)((nodeGroup === null || nodeGroup === void 0 ? void 0 : nodeGroup.image) || (config === null || config === void 0 ? void 0 : config.relaychain.default_image) || constants_1.DEFAULT_IMAGE); } if ((config === null || config === void 0 ? void 0 : config.parachains) && isIterable(config === null || config === void 0 ? void 0 : config.parachains)) for (const parachain of config.parachains) { if ((parachain === null || parachain === void 0 ? void 0 : parachain.collator_groups) && isIterable(parachain === null || parachain === void 0 ? void 0 : parachain.collator_groups)) for (const collatorGroup of (parachain === null || parachain === void 0 ? void 0 : parachain.collator_groups) || []) { (0, utils_1.validateImageUrl)((collatorGroup === null || collatorGroup === void 0 ? void 0 : collatorGroup.image) || ((_e = config === null || config === void 0 ? void 0 : config.relaychain) === null || _e === void 0 ? void 0 : _e.default_image) || constants_1.DEFAULT_COLLATOR_IMAGE); } if ((parachain === null || parachain === void 0 ? void 0 : parachain.collators) && isIterable(parachain === null || parachain === void 0 ? void 0 : parachain.collators)) for (const collatorConfig of (parachain === null || parachain === void 0 ? void 0 : parachain.collators) || []) { (0, utils_1.validateImageUrl)((collatorConfig === null || collatorConfig === void 0 ? void 0 : collatorConfig.image) || constants_1.DEFAULT_COLLATOR_IMAGE); } } }; function generateNetworkSpec(config) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; let globalOverrides = []; if (config.relaychain.default_overrides) { globalOverrides = yield Promise.all(config.relaychain.default_overrides.map((override) => __awaiter(this, void 0, void 0, function* () { const valid_local_path = yield getLocalOverridePath(config.configBasePath, override.local_path); return { local_path: valid_local_path, remote_name: override.remote_name, }; }))); } const networkSpec = { configBasePath: config.configBasePath, relaychain: { defaultImage: config.relaychain.default_image || constants_1.DEFAULT_IMAGE, defaultCommand: config.relaychain.default_command || constants_1.DEFAULT_COMMAND, defaultArgs: config.relaychain.default_args || [], defaultKeystoreKeyTypes: config.relaychain.default_keystore_key_types || constants_1.DEFAULT_KEYSTORE_KEY_TYPES, randomNominatorsCount: ((_a = config.relaychain) === null || _a === void 0 ? void 0 : _a.random_nominators_count) || 0, maxNominations: ((_b = config.relaychain) === null || _b === void 0 ? void 0 : _b.max_nominations) || constants_1.DEFAULT_MAX_NOMINATIONS, nodes: [], chain: config.relaychain.chain || constants_1.DEFAULT_CHAIN, force_decorator: config.relaychain.force_decorator, overrides: globalOverrides, defaultResources: config.relaychain.default_resources, defaultPrometheusPrefix: config.relaychain.default_prometheus_prefix || constants_1.DEFAULT_PROMETHEUS_PREFIX, defaultSubstrateCliArgsVersion: config.relaychain.default_substrate_cli_args_version, delayNetworkSettings: config.relaychain.default_delay_network_settings || ((_c = config.settings) === null || _c === void 0 ? void 0 : _c.global_delay_network_global_settings), useStashForValidators: config.relaychain.use_stash_for_validators != false, // false IFF is explicit false }, parachains: [], }; // check all imageURLs for validity // TODO: These checks should be against all config items that needs check configurationFileChecks(config); if (config.relaychain.genesis) networkSpec.relaychain.genesis = config.relaychain.genesis; const chainName = config.relaychain.chain || constants_1.DEFAULT_CHAIN; if (config.relaychain.default_db_snapshot) networkSpec.relaychain.defaultDbSnapshot = config.relaychain.default_db_snapshot; // settings networkSpec.settings = Object.assign({ timeout: constants_1.DEFAULT_GLOBAL_TIMEOUT, enable_tracing: true, node_verifier: "Metric" }, (config.settings ? config.settings : {})); // default provider if (!networkSpec.settings.provider) networkSpec.settings.provider = "kubernetes"; // if we don't have a path to the chain-spec leave undefined to create if (config.relaychain.chain_spec_path) { networkSpec.relaychain.chainSpecPath = yield resolveChainSpecPath(config.relaychain.chain_spec_path, "relaychain"); } // even if we have a chain_spec_path we need to set // the command to generate the raw version networkSpec.relaychain.chainSpecCommand = config.relaychain.chain_spec_command ? config.relaychain.chain_spec_command : constants_1.DEFAULT_CHAIN_SPEC_COMMAND.replace("{{DEFAULT_COMMAND}}", networkSpec.relaychain.defaultCommand); const relayChainBootnodes = []; for (const node of config.relaychain.nodes || []) { const nodeSetup = yield getNodeFromConfig(networkSpec, node, relayChainBootnodes, globalOverrides, node.name); networkSpec.relaychain.nodes.push(nodeSetup); } for (const nodeGroup of config.relaychain.node_groups || []) { for (let i = 0; i < nodeGroup.count; i++) { const node = { // Replace whitespaces with dashes for node names // see https://github.com/paritytech/zombienet/issues/1659 name: `${nodeGroup.name.replaceAll(" ", "-")}-${i}`, image: nodeGroup.image || networkSpec.relaychain.defaultImage, command: nodeGroup.command, args: sanitizeArgs(nodeGroup.args || []), validator: true, // groups are always validators invulnerable: false, balance: constants_1.DEFAULT_BALANCE, env: nodeGroup.env, overrides: nodeGroup.overrides, resources: nodeGroup.resources || networkSpec.relaychain.defaultResources, db_snapshot: nodeGroup.db_snapshot, prometheus_prefix: nodeGroup.prometheus_prefix || networkSpec.relaychain.defaultPrometheusPrefix, substrate_cli_args_version: nodeGroup.substrate_cli_args_version || networkSpec.relaychain.defaultSubstrateCliArgsVersion, }; const nodeSetup = yield getNodeFromConfig(networkSpec, node, relayChainBootnodes, globalOverrides, nodeGroup.name); networkSpec.relaychain.nodes.push(nodeSetup); } } if (networkSpec.relaychain.nodes.length < 1) { throw new Error("No NODE defined in config, please review."); } const validatorCount = networkSpec.relaychain.nodes.filter((node) => node.validator).length; if (networkSpec.relaychain.maxNominations > validatorCount && networkSpec.relaychain.randomNominatorsCount > 0) { networkSpec.relaychain.maxNominations = validatorCount; } if (config.parachains && config.parachains.length) { for (const parachain of config.parachains) { const para = (0, chain_decorators_1.whichChain)(parachain.chain || parachain.chain_spec_path || "", parachain.force_decorator); let computedStatePath, computedStateCommand, computedWasmPath, computedWasmCommand; const bootnodes = relayChainBootnodes; // parachain_relaychain const paraChainName = (parachain.chain ? parachain.chain + "_" : "") + chainName; // IF is defined use that value // else check if the command is one off undying/adder otherwise true const isCumulusBased = parachain.cumulus_based !== undefined ? parachain.cumulus_based : ![constants_1.DEFAULT_ADDER_COLLATOR_BIN, constants_1.UNDYING_COLLATOR_BIN].includes(getFirstCollatorCommand(parachain)); // collator could by defined in groups or // just using one collator definition const collators = []; const collatorConfigs = parachain.collator ? [parachain.collator] : []; if (parachain.collators) collatorConfigs.push(...parachain.collators); for (const collatorConfig of collatorConfigs) { collators.push(yield getCollatorNodeFromConfig(networkSpec, collatorConfig, parachain, paraChainName, para, bootnodes, isCumulusBased, collatorConfig.name)); } for (const collatorGroup of parachain.collator_groups || []) { for (let i = 0; i < collatorGroup.count; i++) { const node = { // Replace whitespaces with dashes for node names // see https://github.com/paritytech/zombienet/issues/1659 name: `${collatorGroup.name.replaceAll(" ", "-")}-${i}`, image: collatorGroup.image || constants_1.DEFAULT_COLLATOR_IMAGE, command: collatorGroup.command || constants_1.DEFAULT_CUMULUS_COLLATOR_BIN, args: sanitizeArgs(collatorGroup.args || [], { "listen-addr": 2 }), validator: true, // groups are always validators invulnerable: false, balance: constants_1.DEFAULT_BALANCE, env: collatorGroup.env, overrides: collatorGroup.overrides, resources: collatorGroup.resources || networkSpec.relaychain.defaultResources, }; if (collatorGroup.substrate_cli_args_version) node.substrate_cli_args_version = collatorGroup.substrate_cli_args_version; collators.push(yield getCollatorNodeFromConfig(networkSpec, node, parachain, paraChainName, para, bootnodes, isCumulusBased, collatorGroup.name)); } } // use the first collator for state/wasm generation const firstCollator = collators[0]; if (!firstCollator) throw new Error(`No Collator defined for parachain ${parachain.id}, please review.`); const collatorBinary = firstCollator.commandWithArgs ? firstCollator.commandWithArgs.split(" ")[0] : firstCollator.command || constants_1.DEFAULT_CUMULUS_COLLATOR_BIN; // `test-parachain` doesn't support output file yet const exportCmdSupportOutputFile = isCumulusBased && collatorBinary !== "test-parachain"; if (parachain.genesis_state_path) { const genesisStatePath = (0, path_1.resolve)(process.cwd(), parachain.genesis_state_path); if (!fs_1.default.existsSync(genesisStatePath)) { console.error(utils_1.decorators.red(`Genesis state file path provided does not exist: ${genesisStatePath}`)); process.exit(1); } else { computedStatePath = genesisStatePath; } } else { // NOTE: the subcommand to execute will be set later based on `substrateCliArgsVersion`. computedStateCommand = parachain.genesis_state_generator ? parachain.genesis_state_generator : `${collatorBinary} {{GENESIS_GENERATE_SUBCOMMAND}}`; // TODO: we should remove this conditional ones // https://github.com/paritytech/polkadot-sdk/pull/2375 and // https://github.com/paritytech/polkadot-sdk/pull/2370 // and the jobs tha use a previous version of the collators reach those fixes. computedStateCommand += `${exportCmdSupportOutputFile ? "" : " >"} {{CLIENT_REMOTE_DIR}}/${constants_1.GENESIS_STATE_FILENAME}`; } if (parachain.genesis_wasm_path) { const genesisWasmPath = (0, path_1.resolve)(process.cwd(), parachain.genesis_wasm_path); if (!fs_1.default.existsSync(genesisWasmPath)) { console.error(utils_1.decorators.red(`Genesis state file path provided does not exist: ${genesisWasmPath}`)); process.exit(1); } else { computedWasmPath = genesisWasmPath; } } else { computedWasmCommand = parachain.genesis_wasm_generator ? parachain.genesis_wasm_generator : `${collatorBinary} ${constants_1.DEFAULT_WASM_GENERATE_SUBCOMMAND}`; // TODO: we should remove this conditional ones // https://github.com/paritytech/polkadot-sdk/pull/2375 and // https://github.com/paritytech/polkadot-sdk/pull/2370 // and the jobs tha use a previous version of the collators reach those fixes. computedWasmCommand += `${exportCmdSupportOutputFile ? "" : " >"} {{CLIENT_REMOTE_DIR}}/${constants_1.GENESIS_WASM_FILENAME}`; } let parachainSetup = { id: parachain.id, name: getUniqueName(parachain.id.toString()), para, cumulusBased: isCumulusBased, defaultArgs: parachain.default_args || [], defaultSubstrateCliArgsVersion: parachain.default_substrate_cli_args_version, addToGenesis: parachain.add_to_genesis === undefined ? true : parachain.add_to_genesis, // add by default registerPara: parachain.register_para === undefined ? true : parachain.register_para, // register by default onboardAsParachain: parachain.onboard_as_parachain !== undefined ? parachain.onboard_as_parachain : true, // onboard by default withCustomProps: parachain.with_custom_props !== undefined ? parachain.with_custom_props : false, // don't customize by default collators, }; if (parachain.chain) parachainSetup.chain = parachain.chain; // if we don't have a path to the chain-spec leave undefined to create if (parachain.chain_spec_path) { parachainSetup.chainSpecPath = yield resolveChainSpecPath(parachain.chain_spec_path, `parachain id: ${parachain.id}`); } // even if we have a chain_spec_path we need to set // the command to generate the raw version parachainSetup.chainSpecCommand = parachain.chain_spec_command ? parachain.chain_spec_command : `${collatorBinary} build-spec ${parachain.chain ? "--chain {{chainName}}" : ""} --disable-default-bootnode`; parachainSetup = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, parachainSetup), (parachain.balance ? { balance: parachain.balance } : {})), (computedWasmPath ? { genesisWasmPath: computedWasmPath } : {})), (computedWasmCommand ? { genesisWasmGenerator: computedWasmCommand } : {})), (computedStatePath ? { genesisStatePath: computedStatePath } : {})), (computedStateCommand ? { genesisStateGenerator: computedStateCommand } : {})), (parachain.genesis ? { genesis: parachain.genesis } : {})); networkSpec.parachains.push(parachainSetup); } } networkSpec.types = config.types ? config.types : {}; if (config.hrmp_channels) networkSpec.hrmp_channels = config.hrmp_channels; return networkSpec; }); } // TODO: move this fn to other module. function generateBootnodeSpec(config) { return __awaiter(this, void 0, void 0, function* () { const provider = config.settings.provider; const ports = yield getPorts(provider, {}); const externalPorts = yield getExternalPorts(provider, ports, {}); // In native provider bind only localhost and not use --rpc-external const args = provider != "native" ? ["--listen-addr", "/ip4/0.0.0.0/tcp/30333/ws", "--rpc-external"] : ["--listen-addr", "/ip4/127.0.0.1/tcp/30333/ws"]; const nodeSetup = Object.assign(Object.assign({ name: "bootnode", key: "0000000000000000000000000000000000000000000000000000000000000001", command: config.relaychain.defaultCommand || constants_1.DEFAULT_COMMAND, image: config.relaychain.defaultImage || constants_1.DEFAULT_IMAGE, chain: config.relaychain.chain, validator: false, invulnerable: false, args, env: [], bootnodes: [], telemetryUrl: "", prometheus: true, overrides: [], zombieRole: sharedTypes_1.ZombieRole.BootNode, imagePullPolicy: config.settings.image_pull_policy || "Always" }, ports), { externalPorts }); return nodeSetup; }); } const mUsedNames = {}; const mParasUsedNames = {}; function sanitizeName(name) { // Transform whitespaces to dashes in name, since // whitespaces in names are not supported // see https://github.com/paritytech/zombienet/issues/1659 return name.replaceAll(" ", "-"); } function getUniqueName(name, paraId) { // Transform whitespaces to dashes in name, since // whitespaces in names are not supported // see https://github.com/paritytech/zombienet/issues/1659 name = sanitizeName(name); let uniqueName; if (!mUsedNames[name]) { mUsedNames[name] = 1; uniqueName = name; } else { uniqueName = `${name}-${mUsedNames[name]}`; mUsedNames[name] += 1; } if (paraId) { if (mParasUsedNames[paraId]) { if (mParasUsedNames[paraId][name]) { mParasUsedNames[paraId][name] += 1; } else { // first one mParasUsedNames[paraId][name] = 1; } } else { mParasUsedNames[paraId] = { [name]: 1 }; } } return uniqueName; } function canUseSanitizedNameAsSeed(paraId, name) { if (mParasUsedNames[paraId] && mParasUsedNames[paraId][name] > 1) { console.warn(`⚠️ Can't use '${name}' as seed since is already in use for paraId: ${paraId}`); return false; } return true; } function getLocalOverridePath(configBasePath, definedLocalPath) { return __awaiter(this, void 0, void 0, function* () { // let check if local_path is full or relative let local_real_path = definedLocalPath; if (!fs_1.default.existsSync(definedLocalPath)) { // check relative to config local_real_path = path_1.default.join(configBasePath, definedLocalPath); if (!fs_1.default.existsSync(local_real_path)) throw new Error("Invalid override config, only fullpaths or relative paths (from the config) are allowed"); } return local_real_path; }); } function getCollatorNodeFromConfig(networkSpec, collatorConfig, parachain, chain, // relay-chain para, bootnodes, // parachain bootnodes cumulusBased, group) { return __awaiter(this, void 0, void 0, function* () { let args = sanitizeArgs(parachain.default_args || []); const env = collatorConfig.env ? DEFAULT_ENV.concat(collatorConfig.env) : DEFAULT_ENV; const collatorBinary = collatorConfig.command_with_args ? collatorConfig.command_with_args.split(" ")[0] : collatorConfig.command || constants_1.DEFAULT_CUMULUS_COLLATOR_BIN; const sanitizedName = sanitizeName(collatorConfig.name || "collator"); const collatorName = getUniqueName(sanitizedName, parachain.id); const [decoratedKeysGenerator] = (0, chain_decorators_1.decorate)(para, [keys_1.generateKeyForNode]); // Allow collators to use the same name (and seed) of validators in the RC const seed = canUseSanitizedNameAsSeed(parachain.id, sanitizedName) ? sanitizedName : collatorName; const accountsForNode = yield decoratedKeysGenerator(seed); const provider = networkSpec.settings.provider; const ports = yield getPorts(provider, collatorConfig); const externalPorts = yield getExternalPorts(provider, ports, collatorConfig); // IFF the collator have explicit set the validator field we use that value, // if not we set by default cumulus collators as `validators`, this implies that we will // run those with this flag `--collator`. const isValidator = collatorConfig.validator !== undefined ? collatorConfig.validator : cumulusBased ? true : false; if (collatorConfig.args) args = args.concat(sanitizeArgs(collatorConfig.args, { "listen-addr": 2 }, { nodeName: collatorName, isValidator })); const node = Object.assign(Object.assign({ name: collatorName, key: (0, utils_1.getSha256)(collatorName), accounts: accountsForNode, validator: isValidator, invulnerable: collatorConfig.invulnerable, balance: collatorConfig.balance, image: collatorConfig.image || constants_1.DEFAULT_COLLATOR_IMAGE, command: collatorBinary, commandWithArgs: collatorConfig.command_with_args, keystoreKeyTypes: collatorConfig.keystore_key_types || constants_1.DEFAULT_KEYSTORE_KEY_TYPES, args: args || [], chain, bootnodes, env, telemetryUrl: "", prometheus: prometheusExternal(networkSpec), overrides: [], zombieRole: cumulusBased ? sharedTypes_1.ZombieRole.CumulusCollator : sharedTypes_1.ZombieRole.Collator, parachainId: parachain.id, dbSnapshot: collatorConfig.db_snapshot, imagePullPolicy: networkSpec.settings.image_pull_policy || "Always" }, ports), { externalPorts, p2pCertHash: collatorConfig.p2p_cert_hash, prometheusPrefix: parachain.prometheus_prefix || networkSpec.relaychain.defaultPrometheusPrefix, delayNetworkSettings: collatorConfig.delay_network_settings || parachain.delayNetworkSettings || networkSpec.settings.delayNetworkSettings }); if (group) node.group = group; if (collatorConfig.substrate_cli_args_version || parachain.default_substrate_cli_args_version) node.substrateCliArgsVersion = collatorConfig.substrate_cli_args_version || parachain.default_substrate_cli_args_version || undefined; // will be detected as part of the bootstrap process return node; }); } function getNodeFromConfig(networkSpec, node, relayChainBootnodes, globalOverrides, group) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; const command = node.command ? node.command : networkSpec.relaychain.defaultCommand; const image = node.image || networkSpec.relaychain.defaultImage; let args = sanitizeArgs(networkSpec.relaychain.defaultArgs || []); const env = node.env ? DEFAULT_ENV.concat(node.env) : DEFAULT_ENV; let nodeOverrides = []; if (node.overrides) { nodeOverrides = yield Promise.all(node.overrides.map((override) => __awaiter(this, void 0, void 0, function* () { const valid_local_path = yield getLocalOverridePath(networkSpec.configBasePath, override.local_path); return { local_path: valid_local_path, remote_name: override.remote_name, }; }))); } // by default nodes are validators except for those // set explicit to not be validators. const isValidator = node.validator !== false; const nodeName = getUniqueName(node.name); const accountsForNode = yield (0, keys_1.generateKeyForNode)(nodeName); const provider = networkSpec.settings.provider; const ports = yield getPorts(provider, node); const externalPorts = yield getExternalPorts(provider, ports, node); if (node.args) args = args.concat(sanitizeArgs(node.args, {}, { nodeName, isValidator })); const uniqueArgs = [...new Set(args)]; // build node Setup const nodeSetup = Object.assign(Object.assign({ name: nodeName, key: (0, utils_1.getSha256)(nodeName), accounts: accountsForNode, command: command || constants_1.DEFAULT_COMMAND, commandWithArgs: node.command_with_args, image: image || constants_1.DEFAULT_IMAGE, chain: networkSpec.relaychain.chain, validator: isValidator, invulnerable: node.invulnerable, balance: node.balance || constants_1.DEFAULT_BALANCE, args: uniqueArgs, keystoreKeyTypes: node.keystore_key_types || networkSpec.relaychain.defaultKeystoreKeyTypes || constants_1.DEFAULT_KEYSTORE_KEY_TYPES, env, bootnodes: relayChainBootnodes, telemetryUrl: ((_a = networkSpec.settings) === null || _a === void 0 ? void 0 : _a.telemetry) ? "ws://telemetry:8000/submit 0" : "", telemetry: ((_b = networkSpec.settings) === null || _b === void 0 ? void 0 : _b.telemetry) ? true : false, prometheus: prometheusExternal(networkSpec), overrides: [...globalOverrides, ...nodeOverrides], addToBootnodes: node.add_to_bootnodes ? true : false, resources: node.resources || networkSpec.relaychain.defaultResources, zombieRole: sharedTypes_1.ZombieRole.Node, imagePullPolicy: networkSpec.settings.image_pull_policy || "Always" }, ports), { externalPorts, p2pCertHash: node.p2p_cert_hash, prometheusPrefix: node.prometheus_prefix || networkSpec.relaychain.defaultPrometheusPrefix, delayNetworkSettings: node.delay_network_settings || networkSpec.relaychain.delayNetworkSettings }); if (group) nodeSetup.group = group; const dbSnapshot = node.db_snapshot ? node.db_snapshot : networkSpec.relaychain.defaultDbSnapshot || null; if (dbSnapshot) nodeSetup.dbSnapshot = dbSnapshot; if (node.substrate_cli_args_version || networkSpec.relaychain.defaultSubstrateCliArgsVersion) nodeSetup.substrateCliArgsVersion = node.substrate_cli_args_version || networkSpec.relaychain.defaultSubstrateCliArgsVersion || undefined; // will be detected as part of the bootstrap process return nodeSetup; }); } function sanitizeArgs(args, extraArgsToRemove = {}, context) { // Do NOT filter any argument to the internal full-node of the collator const augmentedArgsToRemove = Object.assign(Object.assign({}, constants_1.ARGS_TO_REMOVE), extraArgsToRemove); let removeNext = false; const separatorIndex = args.indexOf("--"); const filteredArgs = args .slice(0, separatorIndex >= 0 ? separatorIndex : args.length) .filter((arg) => { if (removeNext) { removeNext = false; return false; } const argParsed = arg === "-d" ? "d" : arg.replace(/--/g, ""); if (augmentedArgsToRemove[argParsed]) { // Don't sanitize `--<dev_account>` flags // IFF the node name is one of the dev accounts and is set to be a validator // see: https://github.com/paritytech/zombienet/issues/1448 if (context && argParsed === context.nodeName && context.isValidator && constants_1.DEV_ACCOUNTS.includes(argParsed)) return true; if (augmentedArgsToRemove[argParsed] === 2) removeNext = true; return false; } else { return true; } }); // put the internal full-node args again if (separatorIndex >= 0) { filteredArgs.push(...args.slice(separatorIndex)); } return filteredArgs; } function getPorts(provider, nodeSetup) { return __awaiter(this, void 0, void 0, function* () { let ports = constants_1.DEFAULT_PORTS; if (nodeSetup.ws_port) { console.warn("'ws-port flag was deprecated in https://github.com/paritytech/substrate/pull/13384 (v0.9.43).\nIf you are using a recent version please use 'rpc_port' to set the port"); } if (provider === "native") { ports = { p2pPort: nodeSetup.p2p_port || (yield (0, utils_1.getRandomPort)()), wsPort: nodeSetup.ws_port || (yield (0, utils_1.getRandomPort)()), rpcPort: nodeSetup.rpc_port || (yield (0, utils_1.getRandomPort)()), prometheusPort: nodeSetup.prometheus_port || (yield (0, utils_1.getRandomPort)()), }; } return ports; }); } function getExternalPorts(provider, processPorts, nodeSetup) { return __awaiter(this, void 0, void 0, function* () { if (provider === "native") return processPorts; const ports = { p2pPort: nodeSetup.p2p_port || (yield (0, utils_1.getRandomPort)()), wsPort: nodeSetup.ws_port || (yield (0, utils_1.getRandomPort)()), rpcPort: nodeSetup.rpc_port || (yield (0, utils_1.getRandomPort)()), prometheusPort: nodeSetup.prometheus_port || (yield (0, utils_1.getRandomPort)()), }; return ports; }); } // enable --prometheus-external by default // TODO: fix the `any` to an actual interface const prometheusExternal = (networkSpec) => { var _a; return ((_a = networkSpec.settings) === null || _a === void 0 ? void 0 : _a.prometheus) !== undefined ? networkSpec.settings.prometheus : true; }; function getFirstCollatorCommand(parachain) { var _a, _b; let cmd; if (parachain.collator) { cmd = parachain.collator.command_with_args || parachain.collator.command; } else if ((_a = parachain.collators) === null || _a === void 0 ? void 0 : _a.length) { cmd = parachain.collators[0].command_with_args || parachain.collators[0].command; } else if ((_b = parachain.collator_groups) === null || _b === void 0 ? void 0 : _b.length) { cmd = parachain.collator_groups[0].command; } cmd = cmd || constants_1.DEFAULT_CUMULUS_COLLATOR_BIN; // no command defined we use the default polkadot-parachain. debug(`cmd is ${cmd}`); cmd = cmd.split(" ")[0]; return cmd.split("/").pop(); }