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

Add Parachains to Genesis #86

Merged
merged 2 commits into from
Apr 24, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
94 changes: 61 additions & 33 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ import {
establishHrmpChannel,
} from "./rpc";
import { checkConfig } from "./check";
import { clearAuthorities, addAuthority, changeGenesisParachainsConfiguration } from "./spec";
import {
clearAuthorities,
addAuthority,
changeGenesisParachainsConfiguration,
addGenesisParachain,
} from "./spec";
import { parachainAccount } from "./parachain";
import { ApiPromise } from "@polkadot/api";

import { resolve, dirname } from "path";
import fs from "fs";
import { LaunchConfig } from "./types";
import { LaunchConfig, ParachainConfig } from "./types";

// Special care is needed to handle paths to various files (binaries, spec, config, etc...)
// The user passes the path to `config.json`, and we use that as the starting point for any other
Expand Down Expand Up @@ -58,10 +63,10 @@ function loadTypeDef(types: string | object): object {
}
}

async function main() {
// keep track of registered parachains
let registeredParachains: { [key: string]: boolean } = {};
// keep track of registered parachains
let registeredParachains: { [key: string]: boolean } = {};

async function main() {
// Verify that the `config.json` has all the expected properties.
if (!checkConfig(config)) {
return;
Expand All @@ -80,8 +85,12 @@ async function main() {
await addAuthority(`${chain}.json`, node.name);
}
if (config.relaychain.config) {
await changeGenesisParachainsConfiguration(`${chain}.json`, config.relaychain.config)
await changeGenesisParachainsConfiguration(
`${chain}.json`,
config.relaychain.config
);
}
await addParachainsToGenesis(`${chain}.json`, config.parachains);
// -- End Chain Spec Modify --
await generateChainSpecRaw(relay_chain_bin, chain);
const spec = resolve(`${chain}-raw.json`);
Expand Down Expand Up @@ -115,30 +124,10 @@ async function main() {
);
await startCollator(bin, id, wsPort, port, chain, spec, flags);

// If it isn't registered yet, register the parachain on the relaychain
if (!registeredParachains[id]) {
console.log(`Registering Parachain ${id}`);

// Get the information required to register the parachain on the relay chain.
let genesisState;
let genesisWasm;
try {
genesisState = await exportGenesisState(bin, id, chain);
genesisWasm = await exportGenesisWasm(bin, chain);
} catch (err) {
console.error(err);
process.exit(1);
}

await registerParachain(relayChainApi, id, genesisWasm, genesisState, config.finalization);

registeredParachains[id] = true;

// Allow time for the TX to complete, avoiding nonce issues.
// TODO: Handle nonce directly instead of this.
if (balance) {
await setBalance(relayChainApi, account, balance, config.finalization);
}
// Allow time for the TX to complete, avoiding nonce issues.
// TODO: Handle nonce directly instead of this.
if (balance) {
await setBalance(relayChainApi, account, balance, config.finalization);
}
}

Expand Down Expand Up @@ -170,7 +159,13 @@ async function main() {
}

console.log(`Registering Parachain ${id}`);
await registerParachain(relayChainApi, id, genesisWasm, genesisState, config.finalization);
await registerParachain(
relayChainApi,
id,
genesisWasm,
genesisState,
config.finalization
);

// Allow time for the TX to complete, avoiding nonce issues.
// TODO: Handle nonce directly instead of this.
Expand All @@ -181,7 +176,9 @@ async function main() {
}
if (config.hrmpChannels) {
for (const hrmpChannel of config.hrmpChannels) {
console.log(`Setting Up HRMP Channel ${hrmpChannel.sender} -> ${hrmpChannel.recipient}`);
console.log(
`Setting Up HRMP Channel ${hrmpChannel.sender} -> ${hrmpChannel.recipient}`
);
await ensureOnboarded(relayChainApi, hrmpChannel.sender);
await ensureOnboarded(relayChainApi, hrmpChannel.recipient);

Expand All @@ -192,7 +189,7 @@ async function main() {
recipient,
maxCapacity,
maxMessageSize,
config.finalization,
config.finalization
);
}
}
Expand All @@ -215,6 +212,37 @@ async function ensureOnboarded(relayChainApi: ApiPromise, paraId: number) {
});
}

async function addParachainsToGenesis(
spec: string,
parachains: ParachainConfig[]
) {
console.log("\n⛓ Adding Genesis Parachains");
for (const parachain of parachains) {
const { id, chain } = parachain;
const bin = resolve(config_dir, parachain.bin);
if (!fs.existsSync(bin)) {
console.error("Parachain binary does not exist: ", bin);
process.exit();
}
// If it isn't registered yet, register the parachain in genesis
if (!registeredParachains[id]) {
// Get the information required to register the parachain in genesis.
let genesisState;
let genesisWasm;
try {
genesisState = await exportGenesisState(bin, id, chain);
genesisWasm = await exportGenesisWasm(bin, chain);
} catch (err) {
console.error(err);
process.exit(1);
}

await addGenesisParachain(spec, id, genesisState, genesisWasm, true);
registeredParachains[id] = true;
}
}
}

// Kill all processes when exiting.
process.on("exit", function () {
killAll();
Expand Down
10 changes: 5 additions & 5 deletions src/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export async function registerParachain(
id: string,
wasm: string,
header: string,
finalization: boolean = false,
finalization: boolean = false
) {
return new Promise<void>(async (resolvePromise, reject) => {
await cryptoWaitReady();
Expand Down Expand Up @@ -107,7 +107,7 @@ export async function setBalance(
api: ApiPromise,
who: string,
value: string,
finalization: boolean = false,
finalization: boolean = false
) {
return new Promise<void>(async (resolvePromise, reject) => {
await cryptoWaitReady();
Expand Down Expand Up @@ -157,7 +157,7 @@ export async function establishHrmpChannel(
receiver: number,
maxCapacity: number,
maxMessageSize: number,
finalization: boolean = false,
finalization: boolean = false
) {
return new Promise<void>(async (resolvePromise, reject) => {
await cryptoWaitReady();
Expand All @@ -178,7 +178,7 @@ export async function establishHrmpChannel(
sender,
receiver,
maxCapacity,
maxMessageSize,
maxMessageSize
)
)
.signAndSend(alice, { nonce: nonce, era: 0 }, (result) => {
Expand Down Expand Up @@ -212,7 +212,7 @@ export async function sendHrmpMessage(
api: ApiPromise,
recipient: string,
data: string,
finalization: boolean = false,
finalization: boolean = false
) {
return new Promise<void>(async (resolvePromise, reject) => {
await cryptoWaitReady();
Expand Down
1 change: 1 addition & 0 deletions src/spawn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export async function generateChainSpec(bin: string, chain: string) {

// Output the chainspec of a node using `--raw` from a JSON file.
export async function generateChainSpecRaw(bin: string, chain: string) {
console.log(); // Add a newline in output
return new Promise<void>(function (resolve, reject) {
let args = ["build-spec", "--chain=" + chain + ".json", "--raw"];

Expand Down
69 changes: 57 additions & 12 deletions src/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function clearAuthorities(spec: string) {

let data = JSON.stringify(chainSpec, null, 2);
fs.writeFileSync(spec, data);
console.log(`Starting with a fresh authority set:`);
console.log(`\n🧹 Starting with a fresh authority set...`);
}

// Add additional authorities to chain spec in `session.keys`
Expand All @@ -51,7 +51,7 @@ export async function addAuthority(spec: string, name: string) {
const ed_keyring = new Keyring({ type: "ed25519" });
const ed_account = ed_keyring.createFromUri(`//${nameCase(name)}`);

const ec_keyring = new Keyring({ type: 'ecdsa' });
const ec_keyring = new Keyring({ type: "ecdsa" });
const ec_account = ec_keyring.createFromUri(`//${nameCase(name)}`);

let key = [
Expand All @@ -77,31 +77,76 @@ export async function addAuthority(spec: string, name: string) {

let data = JSON.stringify(chainSpec, null, 2);
fs.writeFileSync(spec, data);
console.log(`Added Genesis Authority ${name}`);
console.log(` 👤 Added Genesis Authority ${name}`);
}

// Add parachains to the chain spec at genesis.
export async function addGenesisParachain(
spec: string,
para_id: string,
head: string,
wasm: string,
parachain: boolean
) {
let rawdata = fs.readFileSync(spec);
let chainSpec = JSON.parse(rawdata);

if (
chainSpec.genesis.runtime.runtime_genesis_config &&
chainSpec.genesis.runtime.runtime_genesis_config.parachainsParas
) {
let paras =
chainSpec.genesis.runtime.runtime_genesis_config.parachainsParas.paras;

let new_para = [
parseInt(para_id),
{
genesis_head: head,
validation_code: wasm,
parachain: parachain,
},
];

paras.push(new_para);

let data = JSON.stringify(chainSpec, null, 2);
fs.writeFileSync(spec, data);
console.log(` ✓ Added Genesis Parachain ${para_id}`);
}
}

// Update the `parachainsConfiguration` in the genesis.
// It will try to match keys which exist within the configuration and update the value.
export async function changeGenesisParachainsConfiguration(spec: string, updates: any) {
export async function changeGenesisParachainsConfiguration(
spec: string,
updates: any
) {
let rawdata = fs.readFileSync(spec);
let chainSpec = JSON.parse(rawdata);

console.log(`\n⚙ Updating Parachains Genesis Configuration`);

if (
chainSpec.genesis.runtime.runtime_genesis_config &&
chainSpec.genesis.runtime.runtime_genesis_config.parachainsConfiguration
) {
let config = chainSpec.genesis.runtime.runtime_genesis_config.parachainsConfiguration.config;
Object.keys(updates).forEach(key => {
let config =
chainSpec.genesis.runtime.runtime_genesis_config.parachainsConfiguration
.config;
Object.keys(updates).forEach((key) => {
if (config.hasOwnProperty(key)) {
config[key] = updates[key];
console.log(`Updated Parachains Configuration [ ${key}: ${config[key]} ]`);
console.log(
` ✓ Updated Parachains Configuration [ ${key}: ${config[key]} ]`
);
} else {
console.error(`!! Bad Parachains Configuration [ ${key}: ${updates[key]} ]`);
console.error(
` ⚠ Bad Parachains Configuration [ ${key}: ${updates[key]} ]`
);
}
});
}

let data = JSON.stringify(chainSpec, null, 2);
fs.writeFileSync(spec, data);
console.log(`Saved Genesis Parachains Configuration`);
let data = JSON.stringify(chainSpec, null, 2);
fs.writeFileSync(spec, data);
}
}