Skip to content
Snippets Groups Projects
Unverified Commit 89b41c57 authored by bader y's avatar bader y Committed by GitHub
Browse files

Add parachain related parameters to `chain-spec-builder` (#4889)


When using with `polkadot-parachain`, you usually need to specify the
`relay_chain` and `para_id` fields in the chain spec.

With this PR it can be achieved by specifying newly added `--para-id`
and `--relay-chain` command line args, e.g:
```
chain-spec-builder create -r _runtime.wasm  --para-id 100 --relay-chain xxx default
```

This was implemented by simple _json_ blobs merging.

Additionally unit tests covering basic functionality were added.

Also adds a fix for not overwriting the chain spec with the default
config each time, swallowing not standard fields is also fixed.

Fixes: #4873

---------

Co-authored-by: default avatarSebastian Kunert <skunert49@gmail.com>
Co-authored-by: default avatarMichal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com>
parent 4d2f7932
Branches
No related merge requests found
Pipeline #495296 waiting for manual action with stages
in 34 minutes and 9 seconds
Showing
with 839 additions and 130 deletions
......@@ -21195,8 +21195,10 @@ dependencies = [
"clap 4.5.11",
"log",
"sc-chain-spec",
"serde",
"serde_json",
"sp-tracing 16.0.0",
"substrate-test-runtime",
]
 
[[package]]
......
//! # What is chain-spec.
//! # What is a chain specification
//!
//! A chain specification file defines the set of properties that are required to run the node as
//! part of the chain. The chain specification consists of two main parts:
......@@ -165,8 +165,10 @@
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", list_presets)]
//! ## Displaying preset with given name
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", get_preset)]
//! ## Building chain-spec using given preset
//! ## Building a solo chain-spec (the default) using given preset
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", generate_chain_spec)]
//! ## Building a parachain chain-spec using given preset
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", generate_para_chain_spec)]
//!
//! [`RuntimeGenesisConfig`]:
//! chain_spec_guide_runtime::runtime::RuntimeGenesisConfig
......
......@@ -104,7 +104,66 @@ fn generate_chain_spec() {
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": null,
"properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" },
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x123",
"patch": {
"bar": {
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"
},
"foo": {
"someEnum": {
"Data2": {
"values": "0x0c10"
}
},
"someInteger": 200
}
}
}
}
});
assert_eq!(output, expected_output, "Output did not match expected");
}
#[test]
#[docify::export]
fn generate_para_chain_spec() {
let output = Command::new(get_chain_spec_builder_path())
.arg("-c")
.arg("/dev/stdout")
.arg("create")
.arg("-c")
.arg("polkadot")
.arg("-p")
.arg("1000")
.arg("-r")
.arg(WASM_FILE_PATH)
.arg("named-preset")
.arg("preset_2")
.output()
.expect("Failed to execute command");
let mut output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
//remove code field for better readability
if let Some(code) = output["genesis"]["runtimeGenesis"].as_object_mut().unwrap().get_mut("code")
{
*code = Value::String("0x123".to_string());
}
let expected_output = json!({
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"relay_chain": "polkadot",
"para_id": 1000,
"properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" },
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
......
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
title: Add CLI options for parachain chain specifications + fix bug for swallowing custom fields
doc:
- audience: Node Operator
description: |
Parachain ID and relay chain can be specified via the CLI arguments for when creating a chain spec.
A bug that also swallowed custom fields outside of the default config has also been fixed.
crates:
- name: staging-chain-spec-builder
bump: major
......@@ -28,4 +28,8 @@ clap = { features = ["derive"], workspace = true }
log = { workspace = true, default-features = true }
sc-chain-spec = { features = ["clap"], workspace = true, default-features = true }
serde_json = { workspace = true, default-features = true }
serde = { workspace = true, default-features = true }
sp-tracing = { workspace = true, default-features = true }
[dev-dependencies]
substrate-test-runtime = { workspace = true }
......@@ -16,19 +16,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use chain_spec_builder::{
generate_chain_spec_for_runtime, AddCodeSubstituteCmd, ChainSpecBuilder, ChainSpecBuilderCmd,
ConvertToRawCmd, DisplayPresetCmd, ListPresetsCmd, UpdateCodeCmd, VerifyCmd,
};
use chain_spec_builder::ChainSpecBuilder;
use clap::Parser;
use sc_chain_spec::{
set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec, GenericChainSpec,
GenesisConfigBuilderRuntimeCaller,
};
use staging_chain_spec_builder as chain_spec_builder;
use std::fs;
type ChainSpec = GenericChainSpec<(), ()>;
//avoid error message escaping
fn main() {
......@@ -42,99 +32,5 @@ fn inner_main() -> Result<(), String> {
sp_tracing::try_init_simple();
let builder = ChainSpecBuilder::parse();
let chain_spec_path = builder.chain_spec_path.to_path_buf();
match builder.command {
ChainSpecBuilderCmd::Create(cmd) => {
let chain_spec_json = generate_chain_spec_for_runtime(&cmd)?;
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
},
ChainSpecBuilderCmd::UpdateCode(UpdateCodeCmd {
ref input_chain_spec,
ref runtime_wasm_path,
}) => {
let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?;
let mut chain_spec_json =
serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(false)?)
.map_err(|e| format!("Conversion to json failed: {e}"))?;
update_code_in_json_chain_spec(
&mut chain_spec_json,
&fs::read(runtime_wasm_path.as_path())
.map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..],
);
let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json)
.map_err(|e| format!("to pretty failed: {e}"))?;
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
},
ChainSpecBuilderCmd::AddCodeSubstitute(AddCodeSubstituteCmd {
ref input_chain_spec,
ref runtime_wasm_path,
block_height,
}) => {
let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?;
let mut chain_spec_json =
serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(false)?)
.map_err(|e| format!("Conversion to json failed: {e}"))?;
set_code_substitute_in_json_chain_spec(
&mut chain_spec_json,
&fs::read(runtime_wasm_path.as_path())
.map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..],
block_height,
);
let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json)
.map_err(|e| format!("to pretty failed: {e}"))?;
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
},
ChainSpecBuilderCmd::ConvertToRaw(ConvertToRawCmd { ref input_chain_spec }) => {
let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?;
let chain_spec_json =
serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(true)?)
.map_err(|e| format!("Conversion to json failed: {e}"))?;
let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json)
.map_err(|e| format!("Conversion to pretty failed: {e}"))?;
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
},
ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec }) => {
let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?;
let _ = serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(true)?)
.map_err(|e| format!("Conversion to json failed: {e}"))?;
},
ChainSpecBuilderCmd::ListPresets(ListPresetsCmd { runtime_wasm_path }) => {
let code = fs::read(runtime_wasm_path.as_path())
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
let caller: GenesisConfigBuilderRuntimeCaller =
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
let presets = caller
.preset_names()
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
let presets: Vec<String> = presets
.into_iter()
.map(|preset| {
String::from(
TryInto::<&str>::try_into(&preset)
.unwrap_or_else(|_| "cannot display preset id")
.to_string(),
)
})
.collect();
println!("{}", serde_json::json!({"presets":presets}).to_string());
},
ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime_wasm_path, preset_name }) => {
let code = fs::read(runtime_wasm_path.as_path())
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
let caller: GenesisConfigBuilderRuntimeCaller =
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
let preset = caller
.get_named_preset(preset_name.as_ref())
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
println!("{preset}");
},
};
Ok(())
builder.run()
}
......@@ -117,11 +117,18 @@
//! [sp-genesis-builder-list]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names
//! [sp-genesis-builder-get-preset]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset
use std::{fs, path::PathBuf};
use clap::{Parser, Subcommand};
use sc_chain_spec::{ChainType, GenericChainSpec, GenesisConfigBuilderRuntimeCaller};
use sc_chain_spec::{
json_patch, set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec, ChainType,
GenericChainSpec, GenesisConfigBuilderRuntimeCaller,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{
borrow::Cow,
fs,
path::{Path, PathBuf},
};
/// A utility to easily create a chain spec definition.
#[derive(Debug, Parser)]
......@@ -158,9 +165,15 @@ pub struct CreateCmd {
/// The chain type.
#[arg(value_enum, short = 't', default_value = "live")]
chain_type: ChainType,
/// The para ID for your chain.
#[arg(long, value_enum, short = 'p', requires = "relay_chain")]
pub para_id: Option<u32>,
/// The relay chain you wish to connect to.
#[arg(long, value_enum, short = 'c', requires = "para_id")]
pub relay_chain: Option<String>,
/// The path to runtime wasm blob.
#[arg(long, short)]
runtime_wasm_path: PathBuf,
#[arg(long, short, alias = "runtime-wasm-path")]
runtime: PathBuf,
/// Export chainspec as raw storage.
#[arg(long, short = 's')]
raw_storage: bool,
......@@ -170,6 +183,10 @@ pub struct CreateCmd {
verify: bool,
#[command(subcommand)]
action: GenesisBuildAction,
/// Allows to provide the runtime code blob, instead of reading it from the provided file path.
#[clap(skip)]
code: Option<Cow<'static, [u8]>>,
}
#[derive(Subcommand, Debug, Clone)]
......@@ -220,7 +237,8 @@ pub struct UpdateCodeCmd {
/// Please note that the file will not be updated in-place.
pub input_chain_spec: PathBuf,
/// The path to new runtime wasm blob to be stored into chain-spec.
pub runtime_wasm_path: PathBuf,
#[arg(alias = "runtime-wasm-path")]
pub runtime: PathBuf,
}
/// Add a code substitute in the chain spec.
......@@ -237,7 +255,8 @@ pub struct AddCodeSubstituteCmd {
/// Chain spec to be updated.
pub input_chain_spec: PathBuf,
/// New runtime wasm blob that should replace the existing code.
pub runtime_wasm_path: PathBuf,
#[arg(alias = "runtime-wasm-path")]
pub runtime: PathBuf,
/// The block height at which the code should be substituted.
pub block_height: u64,
}
......@@ -253,16 +272,16 @@ pub struct ConvertToRawCmd {
#[derive(Parser, Debug, Clone)]
pub struct ListPresetsCmd {
/// The path to runtime wasm blob.
#[arg(long, short)]
pub runtime_wasm_path: PathBuf,
#[arg(long, short, alias = "runtime-wasm-path")]
pub runtime: PathBuf,
}
/// Displays given preset
#[derive(Parser, Debug, Clone)]
pub struct DisplayPresetCmd {
/// The path to runtime wasm blob.
#[arg(long, short)]
pub runtime_wasm_path: PathBuf,
#[arg(long, short, alias = "runtime-wasm-path")]
pub runtime: PathBuf,
/// Preset to be displayed. If none is given default will be displayed.
#[arg(long, short)]
pub preset_name: Option<String>,
......@@ -279,18 +298,146 @@ pub struct VerifyCmd {
pub input_chain_spec: PathBuf,
}
/// Processes `CreateCmd` and returns JSON version of `ChainSpec`.
pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result<String, String> {
let code = fs::read(cmd.runtime_wasm_path.as_path())
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
#[derive(Deserialize, Serialize, Clone)]
pub struct ParachainExtension {
/// The relay chain of the Parachain.
pub relay_chain: String,
/// The id of the Parachain.
pub para_id: u32,
}
let chain_type = &cmd.chain_type;
type ChainSpec = GenericChainSpec<()>;
let builder = GenericChainSpec::<()>::builder(&code[..], Default::default())
.with_name(&cmd.chain_name[..])
.with_id(&cmd.chain_id[..])
.with_chain_type(chain_type.clone());
impl ChainSpecBuilder {
/// Executes the internal command.
pub fn run(self) -> Result<(), String> {
let chain_spec_path = self.chain_spec_path.to_path_buf();
match self.command {
ChainSpecBuilderCmd::Create(cmd) => {
let chain_spec_json = generate_chain_spec_for_runtime(&cmd)?;
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
},
ChainSpecBuilderCmd::UpdateCode(UpdateCodeCmd {
ref input_chain_spec,
ref runtime,
}) => {
let mut chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?;
update_code_in_json_chain_spec(
&mut chain_spec_json,
&fs::read(runtime.as_path())
.map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..],
);
let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json)
.map_err(|e| format!("to pretty failed: {e}"))?;
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
},
ChainSpecBuilderCmd::AddCodeSubstitute(AddCodeSubstituteCmd {
ref input_chain_spec,
ref runtime,
block_height,
}) => {
let mut chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?;
set_code_substitute_in_json_chain_spec(
&mut chain_spec_json,
&fs::read(runtime.as_path())
.map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..],
block_height,
);
let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json)
.map_err(|e| format!("to pretty failed: {e}"))?;
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
},
ChainSpecBuilderCmd::ConvertToRaw(ConvertToRawCmd { ref input_chain_spec }) => {
let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?;
let mut genesis_json =
serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(true)?)
.map_err(|e| format!("Conversion to json failed: {e}"))?;
// We want to extract only raw genesis ("genesis::raw" key), and apply it as a patch
// for the original json file. However, the file also contains original plain
// genesis ("genesis::runtimeGenesis") so set it to null so the patch will erase it.
genesis_json.as_object_mut().map(|map| {
map.retain(|key, _| key == "genesis");
map.get_mut("genesis").map(|genesis| {
genesis.as_object_mut().map(|genesis_map| {
genesis_map
.insert("runtimeGenesis".to_string(), serde_json::Value::Null);
});
});
});
let mut org_chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?;
json_patch::merge(&mut org_chain_spec_json, genesis_json);
let chain_spec_json = serde_json::to_string_pretty(&org_chain_spec_json)
.map_err(|e| format!("Conversion to pretty failed: {e}"))?;
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
},
ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec }) => {
let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?;
let _ = serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(true)?)
.map_err(|e| format!("Conversion to json failed: {e}"))?;
},
ChainSpecBuilderCmd::ListPresets(ListPresetsCmd { runtime }) => {
let code = fs::read(runtime.as_path())
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
let caller: GenesisConfigBuilderRuntimeCaller =
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
let presets = caller
.preset_names()
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
let presets: Vec<String> = presets
.into_iter()
.map(|preset| {
String::from(
TryInto::<&str>::try_into(&preset)
.unwrap_or_else(|_| "cannot display preset id")
.to_string(),
)
})
.collect();
println!("{}", serde_json::json!({"presets":presets}).to_string());
},
ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime, preset_name }) => {
let code = fs::read(runtime.as_path())
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
let caller: GenesisConfigBuilderRuntimeCaller =
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
let preset = caller
.get_named_preset(preset_name.as_ref())
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
println!("{preset}");
},
}
Ok(())
}
/// Sets the code used by [`CreateCmd`]
///
/// The file pointed by `CreateCmd::runtime` field will not be read. Provided blob will used
/// instead for chain spec generation.
pub fn set_create_cmd_runtime_code(&mut self, code: Cow<'static, [u8]>) {
match &mut self.command {
ChainSpecBuilderCmd::Create(cmd) => {
cmd.code = Some(code);
},
_ => {
panic!("Overwriting code blob is only supported for CreateCmd");
},
};
}
}
fn process_action<T: Serialize + Clone + Sync + 'static>(
cmd: &CreateCmd,
code: &[u8],
builder: sc_chain_spec::ChainSpecBuilder<T>,
) -> Result<String, String> {
let builder = match cmd.action {
GenesisBuildAction::NamedPreset(NamedPresetCmd { ref preset_name }) =>
builder.with_genesis_config_preset_name(&preset_name),
......@@ -310,7 +457,7 @@ pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result<String, String
},
GenesisBuildAction::Default(DefaultCmd {}) => {
let caller: GenesisConfigBuilderRuntimeCaller =
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
GenesisConfigBuilderRuntimeCaller::new(&code);
let default_config = caller
.get_default_config()
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
......@@ -330,3 +477,59 @@ pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result<String, String
(false, false) => chain_spec.as_json(false),
}
}
impl CreateCmd {
/// Returns the associated runtime code.
///
/// If the code blob was previously set, returns it. Otherwise reads the file.
fn get_runtime_code(&self) -> Result<Cow<'static, [u8]>, String> {
Ok(if let Some(code) = self.code.clone() {
code
} else {
fs::read(self.runtime.as_path())
.map_err(|e| format!("wasm blob shall be readable {e}"))?
.into()
})
}
}
/// Processes `CreateCmd` and returns string represenataion of JSON version of `ChainSpec`.
pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result<String, String> {
let code = cmd.get_runtime_code()?;
let chain_type = &cmd.chain_type;
let mut properties = sc_chain_spec::Properties::new();
properties.insert("tokenSymbol".into(), "UNIT".into());
properties.insert("tokenDecimals".into(), 12.into());
let builder = ChainSpec::builder(&code[..], Default::default())
.with_name(&cmd.chain_name[..])
.with_id(&cmd.chain_id[..])
.with_properties(properties)
.with_chain_type(chain_type.clone());
let chain_spec_json_string = process_action(&cmd, &code[..], builder)?;
if let (Some(para_id), Some(ref relay_chain)) = (cmd.para_id, &cmd.relay_chain) {
let parachain_properties = serde_json::json!({
"relay_chain": relay_chain,
"para_id": para_id,
});
let mut chain_spec_json_blob = serde_json::from_str(chain_spec_json_string.as_str())
.map_err(|e| format!("deserialization a json failed {e}"))?;
json_patch::merge(&mut chain_spec_json_blob, parachain_properties);
Ok(serde_json::to_string_pretty(&chain_spec_json_blob)
.map_err(|e| format!("to pretty failed: {e}"))?)
} else {
Ok(chain_spec_json_string)
}
}
/// Extract any chain spec and convert it to JSON
fn extract_chain_spec_json(input_chain_spec: &Path) -> Result<serde_json::Value, String> {
let chain_spec = &fs::read(input_chain_spec)
.map_err(|e| format!("Provided chain spec could not be read: {e}"))?;
serde_json::from_slice(&chain_spec).map_err(|e| format!("Conversion to json failed: {e}"))
}
{
"bootNodes": [],
"chainType": "Live",
"codeSubstitutes": {
"100": "0x040506"
},
"custom_field": "custom_value",
"genesis": {
"runtimeGenesis": {
"code": "0x010203",
"config": {
"babe": {
"authorities": [],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
"c": [
1,
4
]
}
},
"balances": {
"balances": []
},
"substrateTest": {
"authorities": []
},
"system": {}
}
}
},
"id": "custom",
"name": "Custom",
"para_id": 10101,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"protocolId": null,
"relay_chain": "rococo-local",
"telemetryEndpoints": null
}
\ No newline at end of file
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"relay_chain": "rococo-local",
"para_id": 10101,
"custom_field": "custom_value",
"codeSubstitutes": {},
"genesis": {
"raw": {
"childrenDefault": {},
"top": {
"0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x00",
"0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000",
"0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002",
"0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01",
"0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545",
"0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545",
"0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01",
"0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000",
"0x3a636f6465": "0x010203",
"0x3a65787472696e7369635f696e646578": "0x00000000",
"0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100",
"0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0000000000000000"
}
}
}
}
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x010203",
"config": {
"babe": {
"authorities": [],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
"c": [
1,
4
]
}
},
"balances": {
"balances": []
},
"substrateTest": {
"authorities": []
},
"system": {}
}
}
}
}
{
"name": "test_chain",
"id": "100",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"relay_chain": "rococo-local",
"para_id": 10101,
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x010203",
"config": {
"babe": {
"authorities": [],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
"c": [
1,
4
]
}
},
"balances": {
"balances": []
},
"substrateTest": {
"authorities": []
},
"system": {}
}
}
}
}
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"codeSubstitutes": {},
"genesis": {
"raw": {
"top": {
"0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e",
"0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000",
"0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002",
"0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01",
"0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545",
"0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545",
"0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01",
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080",
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080",
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080",
"0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000",
"0x3a636f6465": "0x010203",
"0x3a65787472696e7369635f696e646578": "0x00000000",
"0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100",
"0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800"
},
"childrenDefault": {}
}
}
}
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x010203",
"config": {
"babe": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
"5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b"
],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
"c": [
2,
4
]
}
},
"balances": {
"balances": [
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
2000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
2000000000000000
],
[
"5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b",
5000000000000000
]
]
},
"substrateTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
"5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b"
]
},
"system": {}
}
}
}
}
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x010203",
"patch": {
"balances": {
"balances": [
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1000000000000000
]
]
},
"substrateTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"
]
}
}
}
}
}
{
"name": "test_chain",
"id": "100",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x010203",
"config": {
"babe": {
"authorities": [],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
"c": [
1,
4
]
}
},
"balances": {
"balances": []
},
"substrateTest": {
"authorities": []
},
"system": {}
}
}
}
}
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x010203",
"patch": {
"balances": {
"balances": [
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1000000000000000
],
[
"5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b",
5000000000000000
]
]
},
"substrateTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
"5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b"
]
}
}
}
}
}
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"relay_chain": "rococo-local",
"para_id": 10101,
"custom_field": "custom_value",
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x040506",
"config": {
"babe": {
"authorities": [],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
"c": [
1,
4
]
}
},
"balances": {
"balances": []
},
"substrateTest": {
"authorities": []
},
"system": {}
}
}
}
}
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"codeSubstitutes": {},
"genesis": {
"raw": {
"top": {
"0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e",
"0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000",
"0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002",
"0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01",
"0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545",
"0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545",
"0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01",
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080",
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080",
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080",
"0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000",
"0x3a636f6465": "0x040506",
"0x3a65787472696e7369635f696e646578": "0x00000000",
"0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100",
"0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800"
},
"childrenDefault": {}
}
}
}
{
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"tokenDecimals": 12,
"tokenSymbol": "UNIT"
},
"relay_chain": "rococo-local",
"para_id": 10101,
"custom_field": "custom_value",
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x010203",
"config": {
"babe": {
"authorities": [],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
"c": [
1,
4
]
}
},
"balances": {
"balances": []
},
"substrateTest": {
"authorities": []
},
"system": {}
}
}
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment