UNPKG

@moonsong-labs/moonwall-cli

Version:

Testing framework for the Moon family of projects

157 lines (154 loc) 6.39 kB
import { blake2AsHex, cancelReferendaWithCouncil, executeProposalWithCouncil } from "./chunk-DTYVZ3AY.js"; import { getRuntimeWasm } from "./chunk-BWH5WBXI.js"; // src/lib/upgrade.ts import "@moonbeam-network/api-augment"; import fs, { readFileSync } from "fs"; import chalk from "chalk"; import { sha256 } from "ethers"; import { alith } from "@moonsong-labs/moonwall-util"; async function upgradeRuntimeChopsticks(context, path) { const rtWasm = readFileSync(path); const rtHex = `0x${rtWasm.toString("hex")}`; const rtHash = blake2AsHex(rtHex); await context.setStorage({ module: "parachainSystem", method: "authorizedUpgrade", methodParams: rtHash }); await context.createBlock(); const api = context.getSubstrateApi(); await api.tx.parachainSystem.enactAuthorizedUpgrade(rtHex).signAndSend(alith); await context.createBlock({ count: 3 }); } async function upgradeRuntime(api, preferences) { const options = { from: alith, waitMigration: true, useGovernance: false, ...preferences }; return new Promise(async (resolve, reject) => { try { const code = fs.readFileSync( await getRuntimeWasm(options.runtimeName, options.runtimeTag, options.localPath) ).toString(); const existingCode = await api.rpc.state.getStorage(":code"); if (existingCode.toString() == code) { reject( `Runtime upgrade with same code: ${existingCode.toString().slice(0, 20)} vs ${code.toString().slice(0, 20)}` ); } let nonce = (await api.rpc.system.accountNextIndex(options.from.address)).toNumber(); if (options.useGovernance) { let proposal = api.tx.parachainSystem.authorizeUpgrade(blake2AsHex(code)); let encodedProposal = proposal.method.toHex(); let encodedHash = blake2AsHex(encodedProposal); const preImageExists = api.query.preimage && await api.query.preimage.statusFor(encodedHash); const democracyPreImageExists = !api.query.preimage && await api.query.democracy.preimages(encodedHash); if (api.query.preimage && preImageExists.isSome && preImageExists.unwrap().isRequested) { process.stdout.write(`Preimage ${encodedHash} already exists ! `); } else if ( // TODO: remove support for democracy preimage support after 2000 !api.query.preimage && democracyPreImageExists.isSome && democracyPreImageExists.unwrap().isAvailable ) { process.stdout.write(`Preimage ${encodedHash} already exists ! `); } else { process.stdout.write( `Registering preimage (${sha256(Buffer.from(code))} [~${Math.floor( code.length / 1024 )} kb])...` ); if (api.query.preimage) { await api.tx.preimage.notePreimage(encodedProposal).signAndSend(options.from, { nonce: nonce++ }); } else { await api.tx.democracy.notePreimage(encodedProposal).signAndSend(options.from, { nonce: nonce++ }); } process.stdout.write(`\u2705 `); } const referendum = await api.query.democracy.referendumInfoOf.entries(); const referendaIndex = api.query.preimage ? referendum.filter( (ref) => ref[1].unwrap().isOngoing && ref[1].unwrap().asOngoing.proposal.isLookup && ref[1].unwrap().asOngoing.proposal.asLookup.hash.toHex() == encodedHash ).map( (ref) => api.registry.createType("u32", ref[0].toU8a().slice(-4)).toNumber() )?.[0] : referendum.filter( (ref) => ref[1].unwrap().isOngoing && ref[1].unwrap().asOngoing.proposalHash.toHex() == encodedHash ).map( (ref) => api.registry.createType("u32", ref[0].toU8a().slice(-4)).toNumber() )?.[0]; if (referendaIndex !== null && referendaIndex !== void 0) { process.stdout.write(`Vote for upgrade already in referendum, cancelling it. `); await cancelReferendaWithCouncil(api, referendaIndex); } await executeProposalWithCouncil(api, encodedHash); nonce = (await api.rpc.system.accountNextIndex(options.from.address)).toNumber(); process.stdout.write(`Enacting authorized upgrade...`); await api.tx.parachainSystem.enactAuthorizedUpgrade(code).signAndSend(options.from, { nonce: nonce++ }); process.stdout.write(`\u2705 `); } else { process.stdout.write( `Sending sudo.setCode (${sha256(Buffer.from(code))} [~${Math.floor( code.length / 1024 )} kb])...` ); const isWeightV1 = !api.registry.createType("Weight").proofSize; await api.tx.sudo.sudoUncheckedWeight( await api.tx.system.setCodeWithoutChecks(code), isWeightV1 ? "1" : { proofSize: 1, refTime: 1 } ).signAndSend(options.from, { nonce: nonce++ }); process.stdout.write(`\u2705 `); } process.stdout.write(`Waiting to apply new runtime (${chalk.red(`~4min`)})...`); let isInitialVersion = true; const unsub = await api.rpc.state.subscribeRuntimeVersion(async (version) => { if (!isInitialVersion) { const blockNumber = (await api.rpc.chain.getHeader()).number.toNumber(); console.log( `\u2705 [${version.implName}-${version.specVersion} ${existingCode.toString().slice(0, 6)}...] [#${blockNumber}]` ); unsub(); const newCode = await api.rpc.state.getStorage(":code"); if (newCode.toString() != code) { reject( `Unexpected new code: ${newCode.toString().slice(0, 20)} vs ${code.toString().slice(0, 20)}` ); } if (options.waitMigration) { const blockToWait = (await api.rpc.chain.getHeader()).number.toNumber() + 1; await new Promise(async (resolve2) => { const subBlocks = await api.rpc.chain.subscribeNewHeads(async (header) => { if (header.number.toNumber() == blockToWait) { subBlocks(); resolve2(blockToWait); } }); }); } resolve(blockNumber); } isInitialVersion = false; }); } catch (e) { console.error(`Failed to setCode`); reject(e); } }); } export { upgradeRuntimeChopsticks, upgradeRuntime };