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

695 lines (694 loc) โ€ข 30.2 kB
"use strict"; 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.specHaveSessionsKeys = specHaveSessionsKeys; exports.clearAuthorities = clearAuthorities; exports.addBalances = addBalances; exports.getNodeKey = getNodeKey; exports.addAuthority = addAuthority; exports.addStaking = addStaking; exports.addCollatorSelection = addCollatorSelection; exports.addParaCustom = addParaCustom; exports.addAuraAuthority = addAuraAuthority; exports.addGrandpaAuthority = addGrandpaAuthority; exports.generateNominators = generateNominators; exports.addParachainToGenesis = addParachainToGenesis; exports.changeGenesisConfig = changeGenesisConfig; exports.addBootNodes = addBootNodes; exports.addHrmpChannelsToGenesis = addHrmpChannelsToGenesis; exports.getRuntimeConfig = getRuntimeConfig; exports.readAndParseChainSpec = readAndParseChainSpec; exports.writeChainSpec = writeChainSpec; exports.isRawSpec = isRawSpec; exports.getChainIdFromSpec = getChainIdFromSpec; exports.customizePlainRelayChain = customizePlainRelayChain; const util_crypto_1 = require("@polkadot/util-crypto"); const utils_1 = require("@zombienet/utils"); const crypto_1 = __importDefault(require("crypto")); const fs_1 = __importDefault(require("fs")); const keys_1 = require("./keys"); const chain_decorators_1 = require("./chain-decorators"); const JSONbig = require("json-bigint")({ useNativeBigInt: true }); const debug = require("debug")("zombie::chain-spec"); const JSONStream = require("JSONStream"); // track 1st staking as default; let stakingBond; // Check if the chainSpec have session keys function specHaveSessionsKeys(chainSpec) { // Check runtime_genesis_config key for rococo compatibility. const runtimeConfig = getRuntimeConfig(chainSpec); return ((runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.session) || (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.palletSession) || (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.authorMapping)); } // Get authority keys from within chainSpec data function getAuthorityKeys(chainSpec, keyType = "session") { const runtimeConfig = getRuntimeConfig(chainSpec); switch (keyType) { case "session": if (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.session) return runtimeConfig.session.keys; break; case "aura": if (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.aura) return runtimeConfig.aura.authorities; break; case "grandpa": if (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.grandpa) return runtimeConfig.grandpa.authorities; break; } const errorMsg = `โš  ${keyType} keys not found in runtimeConfig`; console.error(`\n\t\t ${utils_1.decorators.yellow(errorMsg)}`); } // Remove all existing keys from `session.keys` / aura.authorities / grandpa.authorities function clearAuthorities(specPath) { try { const chainSpec = readAndParseChainSpec(specPath); const runtimeConfig = getRuntimeConfig(chainSpec); // clear keys if (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.session) runtimeConfig.session.keys.length = 0; // clear aura if (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.aura) runtimeConfig.aura.authorities.length = 0; // clear grandpa if (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.grandpa) runtimeConfig.grandpa.authorities.length = 0; // clear collatorSelection if (runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.collatorSelection) runtimeConfig.collatorSelection.invulnerables = []; // clear staking (IFF not a para) // TODO: in the future we should add an option to override or not if ((runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.staking) && !runtimeConfig.parachainInfo) { // Set `stakingBond` IFF there is at least one if (runtimeConfig.staking.stakers[0]) stakingBond = BigInt(runtimeConfig.staking.stakers[0][2]); runtimeConfig.staking.stakers = []; runtimeConfig.staking.invulnerables = []; runtimeConfig.staking.validatorCount = 0; } writeChainSpec(specPath, chainSpec); const logTable = new utils_1.CreateLogTable({ colWidths: [120], }); logTable.pushToPrint([ [utils_1.decorators.green("๐Ÿงน Starting with a fresh authority set...")], ]); } catch (err) { console.error(`\n${utils_1.decorators.red("Fail to clear authorities")}`); throw err; } } function addBalances(specPath, nodes) { return __awaiter(this, void 0, void 0, function* () { try { const chainSpec = readAndParseChainSpec(specPath); const runtimeConfig = getRuntimeConfig(chainSpec); if (!runtimeConfig.balances) { console.error(`\n ๐Ÿšง ${utils_1.decorators.yellow("NO 'balances' key in runtimeConfig, skipping...")} ๐Ÿšง \n`); return; } // Create a balance map const balanceMap = runtimeConfig.balances.balances.reduce((memo, balance) => { memo[balance[0]] = balance[1]; return memo; }, {}); for (const node of nodes) { if (node.balance) { const stashKey = node.accounts.sr_stash.address; const balanceToAdd = stakingBond ? node.validator && node.balance > stakingBond ? node.balance : stakingBond * BigInt(2) // Double the balance we use for stake : node.balance; balanceMap[stashKey] = balanceToAdd; const logLine = `๐Ÿ‘ค Added Balance ${balanceToAdd} for ${utils_1.decorators.green(node.name)} - ${utils_1.decorators.magenta(stashKey)}`; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true, }).pushToPrint([[logLine]]); } } runtimeConfig.balances.balances = Object.entries(balanceMap); writeChainSpec(specPath, chainSpec); } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to add balance for nodes: ${nodes}`)}`); throw err; } }); } function getNodeKey(node, useStash = true) { try { const { sr_stash, sr_account, ed_account, ec_account } = node.accounts; const address = useStash ? sr_stash.address : sr_account.address; const key = [ address, address, { grandpa: ed_account.address, babe: sr_account.address, im_online: sr_account.address, parachain_validator: sr_account.address, authority_discovery: sr_account.address, para_validator: sr_account.address, para_assignment: sr_account.address, beefy: (0, util_crypto_1.encodeAddress)(ec_account.publicKey), aura: sr_account.address, nimbus: sr_account.address, vrf: sr_account.address, mixnet: sr_account.address, bcsv: sr_account.address, ftsv: ed_account.address, }, ]; return key; } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to generate key for node: ${node}`)}`); throw err; } } // Add additional authorities to chain spec in `session.keys` function addAuthority(specPath, node, key) { return __awaiter(this, void 0, void 0, function* () { try { const chainSpec = readAndParseChainSpec(specPath); const { sr_stash } = node.accounts; const keys = getAuthorityKeys(chainSpec); if (!keys) return; keys.push(key); new utils_1.CreateLogTable({ colWidths: [30, 20, 70], }).pushToPrint([ [ utils_1.decorators.cyan("๐Ÿ‘ค Added Genesis Authority"), utils_1.decorators.green(node.name), utils_1.decorators.magenta(sr_stash.address), ], ]); writeChainSpec(specPath, chainSpec); } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to add authority for node: ${node}`)}`); throw err; } }); } /// Add node to staking function addStaking(specPath, node) { return __awaiter(this, void 0, void 0, function* () { try { const chainSpec = readAndParseChainSpec(specPath); const runtimeConfig = getRuntimeConfig(chainSpec); if (!(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.staking)) return; const { sr_stash } = node.accounts; runtimeConfig.staking.stakers.push([ sr_stash.address, sr_stash.address, stakingBond || BigInt(1000000000000), "Validator", ]); runtimeConfig.staking.validatorCount += 1; // add to invulnerables if (node.invulnerable) runtimeConfig.staking.invulnerables.push(sr_stash.address); new utils_1.CreateLogTable({ colWidths: [30, 20, 70], }).pushToPrint([ [ utils_1.decorators.cyan("๐Ÿ‘ค Added Staking"), utils_1.decorators.green(node.name), utils_1.decorators.magenta(sr_stash.address), ], ]); writeChainSpec(specPath, chainSpec); } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to add staking for node: ${node}`)}`); throw err; } }); } /// Add collators function addCollatorSelection(specPath, node) { return __awaiter(this, void 0, void 0, function* () { var _a; try { const chainSpec = readAndParseChainSpec(specPath); const runtimeConfig = getRuntimeConfig(chainSpec); if (!((_a = runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.collatorSelection) === null || _a === void 0 ? void 0 : _a.invulnerables)) return; const { sr_account } = node.accounts; runtimeConfig.collatorSelection.invulnerables.push(sr_account.address); new utils_1.CreateLogTable({ colWidths: [30, 20, 70], }).pushToPrint([ [ utils_1.decorators.cyan("๐Ÿ‘ค Added CollatorSelection "), utils_1.decorators.green(node.name), utils_1.decorators.magenta(sr_account.address), ], ]); writeChainSpec(specPath, chainSpec); } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to add collator: ${node}`)}`); throw err; } }); } function addParaCustom() { return __awaiter(this, void 0, void 0, function* () { /// noop }); } function addAuraAuthority(specPath, name, accounts) { return __awaiter(this, void 0, void 0, function* () { try { const { sr_account } = accounts; const chainSpec = readAndParseChainSpec(specPath); const keys = getAuthorityKeys(chainSpec, "aura"); if (!keys) return; keys.push(sr_account.address); writeChainSpec(specPath, chainSpec); new utils_1.CreateLogTable({ colWidths: [30, 20, 70], }).pushToPrint([ [ utils_1.decorators.cyan("๐Ÿ‘ค Added Genesis Authority"), utils_1.decorators.green(name), utils_1.decorators.magenta(sr_account.address), ], ]); } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to add Aura account for node: ${name}`)}`); throw err; } }); } function addGrandpaAuthority(specPath, name, accounts) { return __awaiter(this, void 0, void 0, function* () { try { const { ed_account } = accounts; const chainSpec = readAndParseChainSpec(specPath); const keys = getAuthorityKeys(chainSpec, "grandpa"); if (!keys) return; keys.push([ed_account.address, 1]); writeChainSpec(specPath, chainSpec); const logLine = `๐Ÿ‘ค Added Genesis Authority (GRANDPA) ${utils_1.decorators.green(name)} - ${utils_1.decorators.magenta(ed_account.address)}`; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true }).pushToPrint([ [logLine], ]); } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to add GrandPa account for node: ${name}`)}`); throw err; } }); } function generateNominators(specPath, randomNominatorsCount, maxNominations, validators) { return __awaiter(this, void 0, void 0, function* () { try { const chainSpec = readAndParseChainSpec(specPath); const runtimeConfig = getRuntimeConfig(chainSpec); if (!(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.staking)) return; let logLine = `๐Ÿ‘ค Generating random Nominators (${utils_1.decorators.green(randomNominatorsCount)})`; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true }).pushToPrint([ [logLine], ]); const maxForRandom = Math.pow(2, 48) - 1; for (let i = 0; i < randomNominatorsCount; i++) { // create account const nom = yield (0, keys_1.generateKeyFromSeed)(`nom-${i}`); // add to balances const balanceToAdd = stakingBond + BigInt(1); runtimeConfig.balances.balances.push([nom.address, balanceToAdd]); // random nominations const count = crypto_1.default.randomInt(maxForRandom) % maxNominations; const nominations = (0, utils_1.getRandom)(validators, count || count + 1); // push to stakers runtimeConfig.staking.stakers.push([ nom.address, nom.address, stakingBond, { Nominator: nominations, }, ]); } writeChainSpec(specPath, chainSpec); logLine = `๐Ÿ‘ค Added random Nominators (${utils_1.decorators.green(randomNominatorsCount)})`; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true }).pushToPrint([ [logLine], ]); } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to generate staking config with count : ${randomNominatorsCount} and max : ${maxNominations}`)}`); throw err; } }); } // Add parachains to the chain spec at genesis. function addParachainToGenesis(specPath_1, para_id_1, head_1, wasm_1) { return __awaiter(this, arguments, void 0, function* (specPath, para_id, head, wasm, parachain = true) { try { const chainSpec = readAndParseChainSpec(specPath); const runtimeConfig = getRuntimeConfig(chainSpec); let paras = undefined; if (runtimeConfig.paras) { paras = runtimeConfig.paras.paras; } // For retro-compatibility with substrate pre Polkadot 0.9.5 else if (runtimeConfig.parachainsParas) { paras = runtimeConfig.parachainsParas.paras; } // The config may not contain paras. Since chainspec allows to contain the RuntimeGenesisConfig patch we can inject it. else { runtimeConfig.paras = { paras: [] }; paras = runtimeConfig.paras.paras; } if (paras) { const new_para = [ parseInt(para_id), [(0, utils_1.readDataFile)(head), (0, utils_1.readDataFile)(wasm), parachain], ]; paras.push(new_para); writeChainSpec(specPath, chainSpec); const logLine = `${utils_1.decorators.green("โœ“ Added Genesis Parachain")} ${para_id}`; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true }).pushToPrint([ [logLine], ]); } else { console.error(`\n${utils_1.decorators.reverse(utils_1.decorators.red(" โš  paras not found in runtimeConfig"))}`); process.exit(1); } } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to add para: ${para_id} to genesis`)}`); throw err; } }); } // Update the runtime config in the genesis. // It will try to match keys which exist within the configuration and update the value. function changeGenesisConfig(specPath, updates) { return __awaiter(this, void 0, void 0, function* () { try { const chainSpec = readAndParseChainSpec(specPath); const msg = `โš™ Updating Chain Genesis Configuration (path: ${specPath})`; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true }).pushToPrint([ [`\n\t ${utils_1.decorators.green(msg)}`], ]); if (chainSpec.genesis) { const config = chainSpec.genesis; findAndReplaceConfig(updates, config); writeChainSpec(specPath, chainSpec); } } catch (err) { console.error(`\n${utils_1.decorators.red("Fail to customize genesis")}`); throw err; } }); } function addBootNodes(specPath, addresses) { return __awaiter(this, void 0, void 0, function* () { let chainSpec; try { chainSpec = readAndParseChainSpec(specPath); } catch (e) { if (e.code !== "ERR_FS_FILE_TOO_LARGE") throw e; // can't customize bootnodes const logLine = ` ๐Ÿšง ${utils_1.decorators.yellow(`Chain Spec file ${specPath} is TOO LARGE to customize (more than 2G).`)} ๐Ÿšง`; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true }).pushToPrint([ [logLine], ]); return; } // prevent dups bootnodes chainSpec.bootNodes = [...new Set(addresses)]; writeChainSpec(specPath, chainSpec); const logTable = new utils_1.CreateLogTable({ colWidths: [120] }); if (addresses.length) { logTable.pushToPrint([ [`${utils_1.decorators.green(chainSpec.name)} โš™ Added Boot Nodes`], [addresses.join("\n")], ]); } else { logTable.pushToPrint([ [`${utils_1.decorators.green(chainSpec.name)} โš™ Clear Boot Nodes`], ]); } }); } function addHrmpChannelsToGenesis(specPath, hrmp_channels) { return __awaiter(this, void 0, void 0, function* () { try { new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true }).pushToPrint([ [`\n\t ${utils_1.decorators.green("Adding Genesis HRMP Channels")}`], ]); const chainSpec = readAndParseChainSpec(specPath); for (const h of hrmp_channels) { const newHrmpChannel = [ h.sender, h.recipient, h.max_capacity, h.max_message_size, ]; const runtimeConfig = getRuntimeConfig(chainSpec); let hrmp = undefined; if (runtimeConfig.hrmp) { hrmp = runtimeConfig.hrmp; } // For retro-compatibility with substrate pre Polkadot 0.9.5 else if (runtimeConfig.parachainsHrmp) { hrmp = runtimeConfig.parachainsHrmp; } else { // No hrmp key in the current chain-spec // let's create the struct and assign runtimeConfig["hrmp"] = { preopenHrmpChannels: [], }; hrmp = runtimeConfig.hrmp; } if (hrmp && hrmp.preopenHrmpChannels) { hrmp.preopenHrmpChannels.push(newHrmpChannel); new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true, }).pushToPrint([ [ utils_1.decorators.green(`โœ“ Added HRMP channel ${h.sender} -> ${h.recipient}`), ], ]); } else { console.error(`${utils_1.decorators.reverse(utils_1.decorators.red(` โš  hrmp not found in runtimeConfig`))}`); process.exit(1); } writeChainSpec(specPath, chainSpec); } } catch (err) { console.error(`\n${utils_1.decorators.red(`Fail to add hrmp channels: ${hrmp_channels}`)}`); throw err; } }); } // Look at the key + values from `obj1` (updates) and try to replace them in `obj2` (config) function findAndReplaceConfig(obj1, obj2) { // create new Object without null prototype, this is a copy from the `config`. const tempObj = Object.assign({}, obj2); // Look at keys of obj1 Object.keys(obj1).forEach((key) => { // See if obj2 also has this key if (tempObj.hasOwnProperty(key)) { // If it goes deeper, recurse... if (obj1[key] !== null && obj1[key] !== undefined && JSON.parse(JSON.stringify(obj1[key])).constructor === Object) { findAndReplaceConfig(obj1[key], obj2[key]); } else { obj2[key] = obj1[key]; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true, }).pushToPrint([ [ `${utils_1.decorators.green("โœ“ Updated Genesis Configuration")} [ key : ${key} ]`, ], ]); debug(`[ ${key}: ${JSON.stringify(obj2[key])} ]`); } } else { // Allow to add keys, see (https://github.com/paritytech/zombienet/issues/1614) const logLine = ` ๐Ÿ–‹ ${utils_1.decorators.yellow(`Key ${key} not present in the current config, adding...`)}`; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true }).pushToPrint([ [logLine], ]); obj2[key] = obj1[key]; new utils_1.CreateLogTable({ colWidths: [120], doubleBorder: true, }).pushToPrint([ [ `${utils_1.decorators.green("โœ“ Updated Genesis Configuration (added key)")} [ key : ${key} ]`, ], ]); debug(`[ ${key}: ${JSON.stringify(obj2[key])} ]`); } }); } function getRuntimeConfig(chainSpec) { var _a, _b, _c; // runtime_genesis_config is no longer in ChainSpec after rococo runtime rework (refer to: https://github.com/paritytech/polkadot-sdk/pull/1256) // ChainSpec may contain a RuntimeGenesisConfigPatch return (((_a = chainSpec.genesis.runtimeGenesis) === null || _a === void 0 ? void 0 : _a.config) || ((_b = chainSpec.genesis.runtimeGenesis) === null || _b === void 0 ? void 0 : _b.patch) || ((_c = chainSpec.genesis.runtime) === null || _c === void 0 ? void 0 : _c.runtime_genesis_config) || chainSpec.genesis.runtime); } function readAndParseChainSpec(specPath) { const rawdata = fs_1.default.readFileSync(specPath); let chainSpec; try { chainSpec = JSONbig.parse(rawdata); return chainSpec; } catch (_a) { console.error(`\n\t\t ${utils_1.decorators.red(" โš  failed to parse the chain spec")}`); process.exit(1); } } function writeChainSpec(specPath, chainSpec) { try { const data = JSONbig.stringify(chainSpec, null, 2); fs_1.default.writeFileSync(specPath, (0, utils_1.convertExponentials)(data)); } catch (_a) { console.error(`\n\t\t ${utils_1.decorators.reverse(utils_1.decorators.red(" โš  failed to write the chain spec with path: "))} ${specPath}`); process.exit(1); } } function isRawSpec(specPath) { return __awaiter(this, void 0, void 0, function* () { return new Promise((res) => { const stream = fs_1.default.createReadStream(specPath, { encoding: "utf8" }); const parser = JSONStream.parse(["genesis", "raw", "top", /^0x/]); stream.pipe(parser); parser.on("data", (e) => { debug(`data: ${e}`); stream.destroy(); return res(true); }); stream.on("end", () => { return res(false); }); }); }); } function getChainIdFromSpec(specPath) { return __awaiter(this, void 0, void 0, function* () { return new Promise((res) => { const stream = fs_1.default.createReadStream(specPath, { encoding: "utf8" }); const parser = JSONStream.parse(["id"]); stream.pipe(parser); parser.on("data", (id) => { debug(`data: ${id}`); stream.destroy(); return res(id); }); stream.on("end", () => { return res(""); }); }); }); } function customizePlainRelayChain(specPath, networkSpec) { return __awaiter(this, void 0, void 0, function* () { try { // Relay-chain spec customization logic const plainRelayChainSpec = readAndParseChainSpec(specPath); const keyType = specHaveSessionsKeys(plainRelayChainSpec) ? "session" : "aura"; // make genesis overrides first. if (networkSpec.relaychain.genesis) { yield changeGenesisConfig(specPath, networkSpec.relaychain.genesis); } // Clear all defaults clearAuthorities(specPath); // add balances for nodes yield addBalances(specPath, networkSpec.relaychain.nodes); // add authorities for nodes const validatorKeys = []; for (const node of networkSpec.relaychain.nodes) { if (node.validator) { validatorKeys.push(node.accounts.sr_stash.address); if (keyType === "session") { const chain = (0, chain_decorators_1.whichChain)(networkSpec.relaychain.chain, networkSpec.relaychain.force_decorator); const [decoratedGetNodeKey] = (0, chain_decorators_1.decorate)(chain, [getNodeKey]); const key = decoratedGetNodeKey(node); yield addAuthority(specPath, node, key); } else { yield addAuraAuthority(specPath, node.name, node.accounts); yield addGrandpaAuthority(specPath, node.name, node.accounts); } yield addStaking(specPath, node); } } if (networkSpec.relaychain.randomNominatorsCount) { yield generateNominators(specPath, networkSpec.relaychain.randomNominatorsCount, networkSpec.relaychain.maxNominations, validatorKeys); } if (networkSpec.hrmp_channels) { yield addHrmpChannelsToGenesis(specPath, networkSpec.hrmp_channels); } } catch (err) { console.log(`\n ${utils_1.decorators.red("Unexpected error: ")} \t ${utils_1.decorators.bright(err)}\n`); } }); } exports.default = { addAuraAuthority, addAuthority, changeGenesisConfig, clearAuthorities, readAndParseChainSpec, specHaveSessionsKeys, writeChainSpec, getNodeKey, addParaCustom, addCollatorSelection, isRawSpec, getChainIdFromSpec, customizePlainRelayChain, };