Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: try-runtime loop #4267

Merged
merged 7 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions bouncer/commands/try_runtime_upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env -S pnpm tsx
// INSTRUCTIONS
// Runs try-runtime upgrade on a particular network of choice. This means it simulates the runtime upgrade, running pre and post upgrade
// hook checks to ensure the upgrade will be successful.
// To increase certainty, you can select to run *all* the migrations until the latest block, or the last N blocks.
// For specific debugging purposes you can run on one specific block, or just the latest block.
//
// PRE-REQUISITES:
// - You must have the `try-runtime-cli` installed: https://paritytech.github.io/try-runtime-cli/try_runtime/
//
// Args
// --block <number, latest, lastN, all>

// Optional args:
// --lastN <number>: If block is lastN, this is the number of blocks to run the migration on. Default is 50.
// --compile: If set, it will compile the runtime to do the upgrade. If false it will use the pre-compiled runtime. Defaults to false.

import path from 'path';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { tryRuntimeUpgrade } from '../shared/try_runtime_upgrade';
import { getChainflipApi, runWithTimeout } from '../shared/utils';

async function main(): Promise<void> {
const argv = yargs(hideBin(process.argv)).boolean('compile').default('compile', false).argv;

const block = argv.block;

if (block === undefined) {
console.error(
'Must provide a block number to try the upgrade at. The options are to use a block number, or `latest` of `last-n <number>` to use the latest block number on the network.',
);
process.exit(-1);
}

const endpoint = process.env.CF_NODE_ENDPOINT ?? 'ws://127.0.0.1:9944';
const chainflipApi = await getChainflipApi();

const lastN = argv.lastN ?? 100;

await tryRuntimeUpgrade(
block,
chainflipApi,
endpoint,
path.dirname(process.cwd()),
argv.compile,
lastN,
);

process.exit(0);
}

runWithTimeout(main(), 1200000).catch((error) => {
console.error(error);
process.exit(-1);
});
84 changes: 84 additions & 0 deletions bouncer/shared/try_runtime_upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// This requirse the try-runtime cli to be installed globally
// https://github.com/paritytech/try-runtime-cli

import { ApiPromise } from '@polkadot/api';
import { execSync } from 'child_process';
import { compileBinaries } from './utils/compile_binaries';

function tryRuntimeCommand(projectRoot: string, blockParam: string, networkUrl: string) {
execSync(
`try-runtime --runtime ${projectRoot}/target/release/wbuild/state-chain-runtime/state_chain_runtime.wasm on-runtime-upgrade --checks all ${blockParam} --uri ${networkUrl}`,
{ stdio: 'ignore' },
);
}

// 4 options:
// - Live chain,
// - Specific block
// - All - goes from block 0 to the latest block when the script was started - this is useful for testing the upgrade on a local chain.
// - last-n, must also specify a number of blocks. This goes backwards from the latest block, running the migration on each block down the chain.
export async function tryRuntimeUpgrade(
block: number | 'latest' | 'all' | 'last-n',
api: ApiPromise,
networkUrl: string,
projectRoot: string,
shouldCompile = true,
lastN = 50,
) {
if (shouldCompile) {
compileBinaries('runtime', projectRoot);
} else {
console.log('Using pre-compiled state chain runtime for try-runtime upgrade.');
}

if (block === 'all') {
const latestBlock = await api.rpc.chain.getBlockHash();

console.log('Running migrations until we reach block with hash: ' + latestBlock);

let blockNumber = 536;
let blockHash = await api.rpc.chain.getBlockHash(blockNumber);
while (!blockHash.eq(latestBlock)) {
blockHash = await api.rpc.chain.getBlockHash(blockNumber);

try {
tryRuntimeCommand(projectRoot, `live --at ${blockHash}`, networkUrl);
console.log(`try-runtime success for block ${blockNumber}, block hash: ${blockHash}`);
} catch (e) {
console.error(`try-runtime failed for block ${blockNumber}, block hash: ${blockHash}`);
console.error(e);
process.exit(-1);
}

blockNumber++;
}
console.log(`Block ${latestBlock} has been reached, exiting.`);
} else if (block === 'last-n') {
console.log(`Running migrations for the last ${lastN} blocks.`);
let blocksProcessed = 0;

let nextHash = await api.rpc.chain.getBlockHash();

while (blocksProcessed < lastN) {
try {
tryRuntimeCommand(projectRoot, `live --at ${nextHash}`, networkUrl);
console.log(`try-runtime success for block hash: ${nextHash}`);
} catch (e) {
console.error(`try-runtime failed for block hash: ${nextHash}`);
console.error(e);
process.exit(-1);
}

const currentBlockHeader = await api.rpc.chain.getHeader(nextHash);
nextHash = currentBlockHeader.parentHash;
blocksProcessed++;
}
} else if (block === 'latest') {
tryRuntimeCommand(projectRoot, 'live', networkUrl);
} else {
const blockHash = await api.rpc.chain.getBlockHash(block);
tryRuntimeCommand(projectRoot, `live --at ${blockHash}`, networkUrl);
}

console.log('try-runtime upgrade successful.');
}
8 changes: 5 additions & 3 deletions bouncer/shared/utils/compile_binaries.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { execSync } from 'child_process';

// Returns the expected next version of the runtime.
// TODO: Maybe only compile with try-runtime if we need to.
export async function compileBinaries(type: 'runtime' | 'all', projectRoot: string) {
if (type === 'all') {
console.log('Building all the binaries...');
execSync(`cd ${projectRoot} && cargo build --release`);
execSync(`cd ${projectRoot} && cargo build --release --features try-runtime`);
} else {
console.log('Building the new runtime...');
execSync(`cd ${projectRoot}/state-chain/runtime && cargo build --release`);
execSync(
`cd ${projectRoot}/state-chain/runtime && cargo build --release --features try-runtime`,
);
}

console.log('Build complete.');
Expand Down
Loading