From 76a0492df7c2315f2f2ef1dc9f8e913e23ffcf36 Mon Sep 17 00:00:00 2001 From: Abhishek Harde <47945971+abhiyana@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:24:09 +0530 Subject: [PATCH] feat: implement add custom sudo key for parachain (#204) * fix: fix issue for robonomics mainnet relay not showing blocks * fix: fix issue litentry parachain testnet not showing blocks * fix: kilt issue and acala karura testnet issue * fix: acala, karura testnet issue * chore: change base for manta parachain * feat: implement add custom sudo key for parachain * refactor: change insert_keys function, from js script to curl command * chore: code clean in parachain.star * refactor: change insertkey function from curl to js script * chore: updated the config.json * refactor: refactor the insert_keys function parameter * refactor: refactor config.json, change sudo_key key position * feat: add functionality to add sepate key wallet for each node * refactor: refactor code in parachain.star, nodes can start with and without keys * refactor: update script to update parachain spec * fix: fix issue if sudo_key, key is not provided in config then it throw error * fix: issue with sudo key while without registratioon is true --------- Co-authored-by: Shanith K K --- config.json | 43 +++++----- parachain/build-spec.star | 15 +++- parachain/parachain.star | 61 +++++++++++--- parachain/parachain_without_registration.star | 9 ++- .../javascript/edit_parachain_plain.js | 79 +++++++++++++++++++ .../static_files/javascript/insert_key.js | 37 +++++++++ 6 files changed, 205 insertions(+), 39 deletions(-) create mode 100644 parachain/static_files/javascript/edit_parachain_plain.js create mode 100644 parachain/static_files/javascript/insert_key.js diff --git a/config.json b/config.json index 78bc894..7ea7125 100644 --- a/config.json +++ b/config.json @@ -11,46 +11,43 @@ { "name": "bob", "node_type": "validator", - "prometheus": true + "prometheus": false } ] }, "parachains": [ - { - "name":"acala", - "nodes": [ - { - "name": "alice", - "node_type": "validator", - "prometheus": false - - }, - { - "name": "bob", - "node_type": "full", - "prometheus": true - } - ] - }, { "name":"frequency", "nodes": [ { "name": "alice", - "node_type": "validator", - "prometheus": false + "node_type": "collator", + "prometheus": false, + "key":{ + "private_phrase":"dizzy rose offer wall social glory debris gift govern seminar almost bicycle" , + "public_key":"5G3vA9UC6J7hPN5Jqfiz4qFNqYAfahHxffbuQf6dcZZx3Yx6" + } }, { "name": "bob", "node_type": "full", - "prometheus": true + "prometheus": false, + "key": { + "private_phrase":"diamond crane pioneer aisle conduct media news cup price olive dust light" , + "public_key":"5GgwsM48CCT7oPUyFTTpE5L1t7Fjpe7D8SDNKxJxug15YEKy" + } } - ] + ], + "sudo_key": { + "private_phrase":"dizzy rose offer wall social glory debris gift govern seminar almost bicycle" , + "public_key":"5G3vA9UC6J7hPN5Jqfiz4qFNqYAfahHxffbuQf6dcZZx3Yx6" + } } ], - "explorer": true, - "without_registration" : true + + "explorer": false, + "without_registration" : false } \ No newline at end of file diff --git a/parachain/build-spec.star b/parachain/build-spec.star index 5c10b95..fbfdfdf 100644 --- a/parachain/build-spec.star +++ b/parachain/build-spec.star @@ -1,7 +1,7 @@ build_spec = import_module("../package_io/build-spec.star") constant = import_module("../package_io/constant.star") -def create_parachain_build_spec_with_para_id(plan, image, binary, chain_name, chain_base, para_id): +def create_parachain_build_spec_with_para_id(plan, image, binary, chain_name, chain_base, para_id, sudo_key, collators_keys): files = { "/app": "configs", } @@ -16,13 +16,19 @@ def create_parachain_build_spec_with_para_id(plan, image, binary, chain_name, ch files = { "/app": "configs", "/build": chain_name + "plain", + "/javascript": "javascript", } + + + run_command = "cd /javascript && npm i && node edit_parachain_plain.js /build/{0}.json {1} \"{2}\" \'{3}\'".format(chain_name, para_id, sudo_key, collators_keys) + plan.print(run_command) plan.run_sh( - run = "sed -e 's/\"parachainId\": *[0-9]\\+/\"parachainId\": {0}/' -e 's/\"para_id\": [0-9]*,/\"para_id\": {0},/' -e 's/\"paraId\": [0-9]*,/\"paraId\": {0},/' -e 's/\"parachain_id\": [0-9]*,/\"parachain_id\": {0},/' /build/{1}.json > /tmp/{1}.json".format(para_id, chain_name), - image = constant.CURL_JQ_IMAGE, + run = run_command, + image = constant.NODE_IMAGE, files = files, - store = [StoreSpec(src = "/tmp/{0}.json".format(chain_name), name = chain_name + "edit")], + store = [StoreSpec(src = "/build/{0}.json".format(chain_name), name = chain_name + "edit")], ) + raw_service = create_raw_build_spec_genisis_state_genisis_wasm_for_parachain(plan, binary, image, chain_name) @@ -42,3 +48,4 @@ def create_raw_build_spec_genisis_state_genisis_wasm_for_parachain(plan, binary, ) return chain_name + "raw" + \ No newline at end of file diff --git a/parachain/parachain.star b/parachain/parachain.star index e2f4240..60f4642 100644 --- a/parachain/parachain.star +++ b/parachain/parachain.star @@ -22,7 +22,15 @@ def start_local_parachain_node(plan, chain_type, parachain, para_id): image = parachain_details["image"] binary = parachain_details["entrypoint"] chain_base = parachain_details["base"][0] - raw_service = build_spec.create_parachain_build_spec_with_para_id(plan, image, binary, chain_name, chain_base, para_id) + + sudo_key = "" + if parachain.get("sudo_key") != None and len(parachain["sudo_key"]) != 0: + sudo_key = parachain["sudo_key"]["private_phrase"] + + public_keys = [node.get("key", {}).get("private_phrase", "") for node in parachain["nodes"] if node.get("key")] + collators_keys = "[" + ", ".join(["\"{}\"".format(key) for key in public_keys]) + "]" + + raw_service = build_spec.create_parachain_build_spec_with_para_id(plan, image, binary, chain_name, chain_base, para_id, sudo_key, collators_keys) parachain_final = {} @@ -41,17 +49,32 @@ def start_local_parachain_node(plan, chain_type, parachain, para_id): ws_port = None if chain_name in constant.WS_PORT: - exec_comexec_commandmand = [ - "/bin/bash", - "-c", - "{0} --base-path=/tmp/{1} --chain=/build/{1}-raw.json --ws-port=9944 --port=30333 --rpc-port=9947 --ws-external --rpc-external --prometheus-external --rpc-cors=all --{2} --collator --rpc-methods=unsafe --force-authoring --execution=wasm -- --chain=/app/raw-polkadot.json --execution=wasm".format(binary, chain_name, node["name"]), - ] + if node.get("key") != None and len(node["key"]) != 0: + exec_comexec_commandmand = [ + "/bin/bash", + "-c", + "{0} --base-path=/tmp/{1} --chain=/build/{1}-raw.json --ws-port=9944 --port=30333 --rpc-port=9947 --ws-external --rpc-external --prometheus-external --rpc-cors=all --name={2} --collator --rpc-methods=unsafe --force-authoring --execution=wasm -- --chain=/app/raw-polkadot.json --execution=wasm".format(binary, chain_name, node["name"]), + ] + else: + exec_comexec_commandmand = [ + "/bin/bash", + "-c", + "{0} --base-path=/tmp/{1} --chain=/build/{1}-raw.json --ws-port=9944 --port=30333 --rpc-port=9947 --ws-external --rpc-external --prometheus-external --rpc-cors=all --{2} --collator --rpc-methods=unsafe --force-authoring --execution=wasm -- --chain=/app/raw-polkadot.json --execution=wasm".format(binary, chain_name, node["name"]), + ] else: - exec_comexec_commandmand = [ - "/bin/bash", - "-c", - "{0} --base-path=/tmp/{1} --chain=/build/{1}-raw.json --rpc-port=9947 --port=30333 --rpc-external --rpc-cors=all --prometheus-external --{2} --collator --rpc-methods=unsafe --force-authoring --execution=wasm -- --chain=/app/raw-polkadot.json --execution=wasm".format(binary, chain_name, node["name"]), - ] + if node.get("key") != None and len(node["key"]) != 0: + exec_comexec_commandmand = [ + "/bin/bash", + "-c", + "{0} --base-path=/tmp/{1} --chain=/build/{1}-raw.json --rpc-port=9947 --port=30333 --rpc-external --rpc-cors=all --prometheus-external --name={2} --collator --rpc-methods=unsafe --force-authoring --execution=wasm -- --chain=/app/raw-polkadot.json --execution=wasm".format(binary, chain_name, node["name"]), + ] + else: + exec_comexec_commandmand = [ + "/bin/bash", + "-c", + "{0} --base-path=/tmp/{1} --chain=/build/{1}-raw.json --rpc-port=9947 --port=30333 --rpc-external --rpc-cors=all --prometheus-external --{2} --collator --rpc-methods=unsafe --force-authoring --execution=wasm -- --chain=/app/raw-polkadot.json --execution=wasm".format(binary, chain_name, node["name"]), + ] + build_file = raw_service parachain_spawn_detail = node_setup.spawn_parachain(plan, node["prometheus"], image, parachain["name"], "{0}-{1}-{2}".format(chain_name, node["name"], chain_type), exec_comexec_commandmand, build_file, rpc_port, prometheus_port, lib2lib_port, ws_port) @@ -72,6 +95,9 @@ def start_local_parachain_node(plan, chain_type, parachain, para_id): parachain_final[parachain_spawn_detail.name] = parachain_detail + if node.get("key") != None and len(node["key"]) != 0: + insert_keys(plan, "aura", node["key"]["private_phrase"], parachain_detail["endpoint"]) + return parachain_final def start_nodes(plan, chain_type, parachains, relay_chain_ip): @@ -239,3 +265,16 @@ def run_testnet_mainnet(plan, chain_type, relaychain_name, parachain): final_parachain_info[node_details.name] = node_info return final_parachain_info + + + +def insert_keys(plan, key_type, private_phrase, uri): + files = { + "/javascript": "javascript", + } + + plan.run_sh( + run = 'cd /javascript && npm i && node insert_key.js "{0}" "{1}" "{2}"'.format(key_type, private_phrase, uri), + image = constant.NODE_IMAGE, + files = files, + ) diff --git a/parachain/parachain_without_registration.star b/parachain/parachain_without_registration.star index f2423a2..bcd2faa 100644 --- a/parachain/parachain_without_registration.star +++ b/parachain/parachain_without_registration.star @@ -22,8 +22,15 @@ def start_nodes(plan, chain_type, relaychain, parachains): binary = parachain_details["entrypoint"] chain_base = parachain_details["base"][0] + sudo_key = "" + if parachain.get("sudo_key") != None and len(parachain["sudo_key"]) != 0: + sudo_key = parachain["sudo_key"]["private_phrase"] + + public_keys = [node.get("key", {}).get("private_phrase", "") for node in parachain["nodes"] if node.get("key")] + collators_keys = "[" + ", ".join(["\"{}\"".format(key) for key in public_keys]) + "]" + # Creating the genisis state genesis wasm and raw build spec for parachain - build_spec.create_parachain_build_spec_with_para_id(plan, image, binary, chain_name, chain_base, para_id) + build_spec.create_parachain_build_spec_with_para_id(plan, image, binary, chain_name, chain_base, para_id, sudo_key, collators_keys) # if parachain are more than one, adding the genisis state genesis wasm to relay(polkadot) chain spec if counter > 0: diff --git a/parachain/static_files/javascript/edit_parachain_plain.js b/parachain/static_files/javascript/edit_parachain_plain.js new file mode 100644 index 0000000..9a667a4 --- /dev/null +++ b/parachain/static_files/javascript/edit_parachain_plain.js @@ -0,0 +1,79 @@ +const fs = require("fs"); +const { Keyring } = require('@polkadot/keyring'); +const { encodeAddress, cryptoWaitReady } = require('@polkadot/util-crypto'); + +async function updateParachainSpec(paraSpecFile, paraId, newSudoKeyPhrase, initialCollatorsPhrase) { + try { + const rawdata = fs.readFileSync(paraSpecFile); + const chainSpec = JSON.parse(rawdata); + const collators = JSON.parse(initialCollatorsPhrase); + + chainSpec.para_id = paraId; + chainSpec.genesis.runtime.parachainInfo.parachainId = paraId; + + + await cryptoWaitReady(); + const keyring = new Keyring({ type: 'sr25519' }); + + let newSudoKey = ''; // Declaring newSudoKey as let instead of const + + if (newSudoKeyPhrase.length > 0) { + const newSudoAccount = keyring.addFromUri(newSudoKeyPhrase); + newSudoKey = newSudoAccount.address; + chainSpec.genesis.runtime.sudo.key = newSudoKey; + + const SESSION_KEYS = [ + newSudoKey, + newSudoKey, + { aura: newSudoKey } + ]; + chainSpec.genesis.runtime.session.keys.push(SESSION_KEYS); + + const BALANCE = [ + newSudoKey, + chainSpec.genesis.runtime.balances.balances[0][1] + ]; + chainSpec.genesis.runtime.balances.balances.push(BALANCE); + console.log("changed sudo key:", newSudoKey); + } + + if (initialCollatorsPhrase.length > 0) { + collators.forEach(collatorPhrase => { + var collatorAccount = keyring.addFromUri(collatorPhrase); + var collator = collatorAccount.address; + //adding this condition to prevent adding duplicate keys + if(collatorPhrase!=newSudoKeyPhrase){ + console.log("updating collator:", collator); + const sessionKey = [ + collator, + collator, + { aura: collator } + ]; + chainSpec.genesis.runtime.session.keys.push(sessionKey); + + const balance = [ + collator, + chainSpec.genesis.runtime.balances.balances[0][1] + ]; + chainSpec.genesis.runtime.balances.balances.push(balance); + + chainSpec.genesis.runtime.collatorSelection.invulnerables.push(collator); + } else{ + chainSpec.genesis.runtime.collatorSelection.invulnerables.push(collator); + } + }); + } + + fs.writeFileSync(paraSpecFile, JSON.stringify(chainSpec, null, 2)); + + console.log("✓ Updated sudo key and session keys in parachain spec"); + } catch (error) { + console.error("Error updating parachain spec:", error.message); + } +} + +const paraSpecFile = process.argv[2]; +const paraId = parseInt(process.argv[3], 10) || 2000; +const newSudoKey = process.argv[4]; +const initialCollators = process.argv[5]; +updateParachainSpec(paraSpecFile, paraId, newSudoKey, initialCollators); diff --git a/parachain/static_files/javascript/insert_key.js b/parachain/static_files/javascript/insert_key.js new file mode 100644 index 0000000..0f679cd --- /dev/null +++ b/parachain/static_files/javascript/insert_key.js @@ -0,0 +1,37 @@ +const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api'); +const { encodeAddress } = require('@polkadot/util-crypto'); +const { createType } = require('@polkadot/types'); +const { u8aToHex } =require("@polkadot/util") +const { decodeAddress } = require("@polkadot/util-crypto") + + + +async function insertKey(keyType, mnemonicOrPrivateKey, providerUrl) { + try { + const wsProvider = new WsProvider(providerUrl); + const api = await ApiPromise.create({ provider: wsProvider }); + const keyring = new Keyring({ type: 'sr25519' }); + const account = keyring.addFromMnemonic(mnemonicOrPrivateKey); + const result = await api.rpc.author.insertKey(keyType, mnemonicOrPrivateKey, u8aToHex(decodeAddress(account.address))); + console.log(`Account ${account.address} inserted successfully!`); + return result; + } catch (error) { + console.error('Error inserting account:', error); + throw error; + } +} + +const keyType = process.argv[2]; +const mnemonicOrPrivateKey = process.argv[3]; +const providerUrl = process.argv[4]; + +(async () => { + try { + await insertKey(keyType, mnemonicOrPrivateKey, providerUrl); + console.log('Script executed successfully!'); + process.exit(0); + } catch (error) { + console.error('Error executing script:', error); + process.exit(1); + } +})();